mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Migrate rackspace/gophercloud -> gophercloud/gophercloud
This change migrates the 'openstack' provider and 'keystone' authenticator plugin to the newer gophercloud/gophercloud library. Note the 'rackspace' provider still uses rackspace/gophercloud. Fixes #30404
This commit is contained in:
		
							
								
								
									
										15
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,15 +0,0 @@
 | 
			
		||||
// Package extensions provides information and interaction with the different extensions available
 | 
			
		||||
// for an OpenStack service.
 | 
			
		||||
//
 | 
			
		||||
// The purpose of OpenStack API extensions is to:
 | 
			
		||||
//
 | 
			
		||||
// - Introduce new features in the API without requiring a version change.
 | 
			
		||||
// - Introduce vendor-specific niche functionality.
 | 
			
		||||
// - Act as a proving ground for experimental functionalities that might be included in a future
 | 
			
		||||
//   version of the API.
 | 
			
		||||
//
 | 
			
		||||
// Extensions usually have tags that prevent conflicts with other extensions that define attributes
 | 
			
		||||
// or resources with the same names, and with core resources and attributes.
 | 
			
		||||
// Because an extension might not be supported by all plug-ins, its availability varies with deployments
 | 
			
		||||
// and the specific plug-in.
 | 
			
		||||
package extensions
 | 
			
		||||
							
								
								
									
										1
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
package extensions
 | 
			
		||||
							
								
								
									
										91
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/fixtures.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										91
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/fixtures.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,91 +0,0 @@
 | 
			
		||||
// +build fixtures
 | 
			
		||||
 | 
			
		||||
package extensions
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	th "github.com/rackspace/gophercloud/testhelper"
 | 
			
		||||
	"github.com/rackspace/gophercloud/testhelper/client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ListOutput provides a single page of Extension results.
 | 
			
		||||
const ListOutput = `
 | 
			
		||||
{
 | 
			
		||||
	"extensions": [
 | 
			
		||||
		{
 | 
			
		||||
			"updated": "2013-01-20T00:00:00-00:00",
 | 
			
		||||
			"name": "Neutron Service Type Management",
 | 
			
		||||
			"links": [],
 | 
			
		||||
			"namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
 | 
			
		||||
			"alias": "service-type",
 | 
			
		||||
			"description": "API for retrieving service providers for Neutron advanced services"
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}`
 | 
			
		||||
 | 
			
		||||
// GetOutput provides a single Extension result.
 | 
			
		||||
const GetOutput = `
 | 
			
		||||
{
 | 
			
		||||
	"extension": {
 | 
			
		||||
		"updated": "2013-02-03T10:00:00-00:00",
 | 
			
		||||
		"name": "agent",
 | 
			
		||||
		"links": [],
 | 
			
		||||
		"namespace": "http://docs.openstack.org/ext/agent/api/v2.0",
 | 
			
		||||
		"alias": "agent",
 | 
			
		||||
		"description": "The agent management extension."
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// ListedExtension is the Extension that should be parsed from ListOutput.
 | 
			
		||||
var ListedExtension = Extension{
 | 
			
		||||
	Updated:     "2013-01-20T00:00:00-00:00",
 | 
			
		||||
	Name:        "Neutron Service Type Management",
 | 
			
		||||
	Links:       []interface{}{},
 | 
			
		||||
	Namespace:   "http://docs.openstack.org/ext/neutron/service-type/api/v1.0",
 | 
			
		||||
	Alias:       "service-type",
 | 
			
		||||
	Description: "API for retrieving service providers for Neutron advanced services",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExpectedExtensions is a slice containing the Extension that should be parsed from ListOutput.
 | 
			
		||||
var ExpectedExtensions = []Extension{ListedExtension}
 | 
			
		||||
 | 
			
		||||
// SingleExtension is the Extension that should be parsed from GetOutput.
 | 
			
		||||
var SingleExtension = &Extension{
 | 
			
		||||
	Updated:     "2013-02-03T10:00:00-00:00",
 | 
			
		||||
	Name:        "agent",
 | 
			
		||||
	Links:       []interface{}{},
 | 
			
		||||
	Namespace:   "http://docs.openstack.org/ext/agent/api/v2.0",
 | 
			
		||||
	Alias:       "agent",
 | 
			
		||||
	Description: "The agent management extension.",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleListExtensionsSuccessfully creates an HTTP handler at `/extensions` on the test handler
 | 
			
		||||
// mux that response with a list containing a single tenant.
 | 
			
		||||
func HandleListExtensionsSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/extensions", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, ListOutput)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleGetExtensionSuccessfully creates an HTTP handler at `/extensions/agent` that responds with
 | 
			
		||||
// a JSON payload corresponding to SingleExtension.
 | 
			
		||||
func HandleGetExtensionSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/extensions/agent", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
		w.WriteHeader(http.StatusOK)
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, GetOutput)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,21 +0,0 @@
 | 
			
		||||
package extensions
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Get retrieves information for a specific extension using its alias.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, alias string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(ExtensionURL(c, alias), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// List returns a Pager which allows you to iterate over the full collection of extensions.
 | 
			
		||||
// It does not accept query parameters.
 | 
			
		||||
func List(c *gophercloud.ServiceClient) pagination.Pager {
 | 
			
		||||
	return pagination.NewPager(c, ListExtensionURL(c), func(r pagination.PageResult) pagination.Page {
 | 
			
		||||
		return ExtensionPage{pagination.SinglePageBase(r)}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/results.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										65
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/results.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,65 +0,0 @@
 | 
			
		||||
package extensions
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetResult temporarily stores the result of a Get call.
 | 
			
		||||
// Use its Extract() method to interpret it as an Extension.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract interprets a GetResult as an Extension.
 | 
			
		||||
func (r GetResult) Extract() (*Extension, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		Extension *Extension `json:"extension"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.Extension, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extension is a struct that represents an OpenStack extension.
 | 
			
		||||
type Extension struct {
 | 
			
		||||
	Updated     string        `json:"updated" mapstructure:"updated"`
 | 
			
		||||
	Name        string        `json:"name" mapstructure:"name"`
 | 
			
		||||
	Links       []interface{} `json:"links" mapstructure:"links"`
 | 
			
		||||
	Namespace   string        `json:"namespace" mapstructure:"namespace"`
 | 
			
		||||
	Alias       string        `json:"alias" mapstructure:"alias"`
 | 
			
		||||
	Description string        `json:"description" mapstructure:"description"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtensionPage is the page returned by a pager when traversing over a collection of extensions.
 | 
			
		||||
type ExtensionPage struct {
 | 
			
		||||
	pagination.SinglePageBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether an ExtensionPage struct is empty.
 | 
			
		||||
func (r ExtensionPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractExtensions(r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, err
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractExtensions accepts a Page struct, specifically an ExtensionPage struct, and extracts the
 | 
			
		||||
// elements into a slice of Extension structs.
 | 
			
		||||
// In other words, a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractExtensions(page pagination.Page) ([]Extension, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		Extensions []Extension `mapstructure:"extensions"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(ExtensionPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.Extensions, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/urls.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/rackspace/gophercloud/openstack/common/extensions/urls.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,13 +0,0 @@
 | 
			
		||||
package extensions
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
// ExtensionURL generates the URL for an extension resource by name.
 | 
			
		||||
func ExtensionURL(c *gophercloud.ServiceClient, name string) string {
 | 
			
		||||
	return c.ServiceURL("extensions", name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListExtensionURL generates the URL for the extensions resource collection.
 | 
			
		||||
func ListExtensionURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL("extensions")
 | 
			
		||||
}
 | 
			
		||||
@@ -1,83 +0,0 @@
 | 
			
		||||
package trust
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/openstack"
 | 
			
		||||
	token3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AuthOptionsExt struct {
 | 
			
		||||
	token3.AuthOptions
 | 
			
		||||
	TrustID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ao AuthOptionsExt) ToAuthOptionsV3Map(c *gophercloud.ServiceClient, scope *token3.Scope) (map[string]interface{}, error) {
 | 
			
		||||
	//Passing scope value to nil to add scope later in this function.
 | 
			
		||||
	authMap, err := ao.AuthOptions.ToAuthOptionsV3Map(c, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	authMap = authMap["auth"].(map[string]interface{})
 | 
			
		||||
 | 
			
		||||
	// Add a "scope" element if a Scope has been provided.
 | 
			
		||||
	if ao.TrustID != "" {
 | 
			
		||||
		// TrustID provided.
 | 
			
		||||
		authMap["scope"] = map[string]interface{}{
 | 
			
		||||
			"OS-TRUST:trust": map[string]interface{}{
 | 
			
		||||
				"id": ao.TrustID,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, token3.ErrScopeEmpty
 | 
			
		||||
	}
 | 
			
		||||
	return map[string]interface{}{"auth": authMap}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AuthenticateV3 explicitly authenticates against the identity v3 service.
 | 
			
		||||
func AuthenticateV3Trust(client *gophercloud.ProviderClient, options AuthOptionsExt) error {
 | 
			
		||||
	return trustv3auth(client, "", options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func trustv3auth(client *gophercloud.ProviderClient, endpoint string, options AuthOptionsExt) error {
 | 
			
		||||
	//In case of Trust TokenId would be Provided so we have to populate the value in service client
 | 
			
		||||
	//to not throw password error,also if it is not provided it will be empty which maintains
 | 
			
		||||
	//the current implementation.
 | 
			
		||||
	client.TokenID = options.AuthOptions.TokenID
 | 
			
		||||
	// Override the generated service endpoint with the one returned by the version endpoint.
 | 
			
		||||
	v3Client := openstack.NewIdentityV3(client)
 | 
			
		||||
	if endpoint != "" {
 | 
			
		||||
		v3Client.Endpoint = endpoint
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// copy the auth options to a local variable that we can change. `options`
 | 
			
		||||
	// needs to stay as-is for reauth purposes
 | 
			
		||||
	v3Options := options
 | 
			
		||||
 | 
			
		||||
	var scope *token3.Scope
 | 
			
		||||
 | 
			
		||||
	result := token3.Create(v3Client, v3Options, scope)
 | 
			
		||||
 | 
			
		||||
	token, err := result.ExtractToken()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	catalog, err := result.ExtractServiceCatalog()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client.TokenID = token.ID
 | 
			
		||||
 | 
			
		||||
	if options.AuthOptions.AllowReauth {
 | 
			
		||||
		client.ReauthFunc = func() error {
 | 
			
		||||
			client.TokenID = ""
 | 
			
		||||
			return trustv3auth(client, endpoint, options)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
 | 
			
		||||
		return openstack.V3EndpointURL(catalog, opts)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/delegate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/delegate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,41 +0,0 @@
 | 
			
		||||
package extensions
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	common "github.com/rackspace/gophercloud/openstack/common/extensions"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Extension is a single OpenStack extension.
 | 
			
		||||
type Extension struct {
 | 
			
		||||
	common.Extension
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult wraps a GetResult from common.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	common.GetResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractExtensions interprets a Page as a slice of Extensions.
 | 
			
		||||
func ExtractExtensions(page pagination.Page) ([]Extension, error) {
 | 
			
		||||
	inner, err := common.ExtractExtensions(page)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	outer := make([]Extension, len(inner))
 | 
			
		||||
	for index, ext := range inner {
 | 
			
		||||
		outer[index] = Extension{ext}
 | 
			
		||||
	}
 | 
			
		||||
	return outer, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves information for a specific extension using its alias.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, alias string) GetResult {
 | 
			
		||||
	return GetResult{common.Get(c, alias)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// List returns a Pager which allows you to iterate over the full collection of extensions.
 | 
			
		||||
// It does not accept query parameters.
 | 
			
		||||
func List(c *gophercloud.ServiceClient) pagination.Pager {
 | 
			
		||||
	return common.List(c)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,168 +0,0 @@
 | 
			
		||||
package floatingips
 | 
			
		||||
 | 
			
		||||
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 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"`
 | 
			
		||||
	FloatingNetworkID string `q:"floating_network_id"`
 | 
			
		||||
	PortID            string `q:"port_id"`
 | 
			
		||||
	FixedIP           string `q:"fixed_ip_address"`
 | 
			
		||||
	FloatingIP        string `q:"floating_ip_address"`
 | 
			
		||||
	TenantID          string `q:"tenant_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
 | 
			
		||||
// floating IP resources. It accepts a ListOpts struct, which allows you to
 | 
			
		||||
// filter and sort the returned collection for greater efficiency.
 | 
			
		||||
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 FloatingIPPage{pagination.LinkedPageBase{PageResult: r}}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateOpts contains all the values needed to create a new floating IP
 | 
			
		||||
// resource. The only required fields are FloatingNetworkID and PortID which
 | 
			
		||||
// refer to the external network and internal port respectively.
 | 
			
		||||
type CreateOpts struct {
 | 
			
		||||
	FloatingNetworkID string
 | 
			
		||||
	FloatingIP        string
 | 
			
		||||
	PortID            string
 | 
			
		||||
	FixedIP           string
 | 
			
		||||
	TenantID          string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	errFloatingNetworkIDRequired = fmt.Errorf("A NetworkID is required")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Create accepts a CreateOpts struct and uses the values provided to create a
 | 
			
		||||
// new floating IP resource. You can create floating IPs on external networks
 | 
			
		||||
// only. If you provide a FloatingNetworkID which refers to a network that is
 | 
			
		||||
// not external (i.e. its `router:external' attribute is False), the operation
 | 
			
		||||
// will fail and return a 400 error.
 | 
			
		||||
//
 | 
			
		||||
// If you do not specify a FloatingIP address value, the operation will
 | 
			
		||||
// automatically allocate an available address for the new resource. If you do
 | 
			
		||||
// choose to specify one, it must fall within the subnet range for the external
 | 
			
		||||
// network - otherwise the operation returns a 400 error. If the FloatingIP
 | 
			
		||||
// address is already in use, the operation returns a 409 error code.
 | 
			
		||||
//
 | 
			
		||||
// You can associate the new resource with an internal port by using the PortID
 | 
			
		||||
// field. If you specify a PortID that is not valid, the operation will fail and
 | 
			
		||||
// return 404 error code.
 | 
			
		||||
//
 | 
			
		||||
// You must also configure an IP address for the port associated with the PortID
 | 
			
		||||
// you have provided - this is what the FixedIP refers to: an IP fixed to a port.
 | 
			
		||||
// Because a port might be associated with multiple IP addresses, you can use
 | 
			
		||||
// the FixedIP field to associate a particular IP address rather than have the
 | 
			
		||||
// API assume for you. If you specify an IP address that is not valid, the
 | 
			
		||||
// operation will fail and return a 400 error code. If the PortID and FixedIP
 | 
			
		||||
// are already associated with another resource, the operation will fail and
 | 
			
		||||
// returns a 409 error code.
 | 
			
		||||
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 | 
			
		||||
	var res CreateResult
 | 
			
		||||
 | 
			
		||||
	// Validate
 | 
			
		||||
	if opts.FloatingNetworkID == "" {
 | 
			
		||||
		res.Err = errFloatingNetworkIDRequired
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Define structures
 | 
			
		||||
	type floatingIP struct {
 | 
			
		||||
		FloatingNetworkID string `json:"floating_network_id"`
 | 
			
		||||
		FloatingIP        string `json:"floating_ip_address,omitempty"`
 | 
			
		||||
		PortID            string `json:"port_id,omitempty"`
 | 
			
		||||
		FixedIP           string `json:"fixed_ip_address,omitempty"`
 | 
			
		||||
		TenantID          string `json:"tenant_id,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
	type request struct {
 | 
			
		||||
		FloatingIP floatingIP `json:"floatingip"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Populate request body
 | 
			
		||||
	reqBody := request{FloatingIP: floatingIP{
 | 
			
		||||
		FloatingNetworkID: opts.FloatingNetworkID,
 | 
			
		||||
		FloatingIP:        opts.FloatingIP,
 | 
			
		||||
		PortID:            opts.PortID,
 | 
			
		||||
		FixedIP:           opts.FixedIP,
 | 
			
		||||
		TenantID:          opts.TenantID,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular floating IP resource based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateOpts contains the values used when updating a floating IP resource. The
 | 
			
		||||
// only value that can be updated is which internal port the floating IP is
 | 
			
		||||
// linked to. To associate the floating IP with a new internal port, provide its
 | 
			
		||||
// ID. To disassociate the floating IP from all ports, provide an empty string.
 | 
			
		||||
type UpdateOpts struct {
 | 
			
		||||
	PortID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update allows floating IP resources to be updated. Currently, the only way to
 | 
			
		||||
// "update" a floating IP is to associate it with a new internal port, or
 | 
			
		||||
// disassociated it from all ports. See UpdateOpts for instructions of how to
 | 
			
		||||
// do this.
 | 
			
		||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
 | 
			
		||||
	type floatingIP struct {
 | 
			
		||||
		PortID *string `json:"port_id"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type request struct {
 | 
			
		||||
		FloatingIP floatingIP `json:"floatingip"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var portID *string
 | 
			
		||||
	if opts.PortID == "" {
 | 
			
		||||
		portID = nil
 | 
			
		||||
	} else {
 | 
			
		||||
		portID = &opts.PortID
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{FloatingIP: floatingIP{PortID: portID}}
 | 
			
		||||
 | 
			
		||||
	// Send request to API
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
 | 
			
		||||
		OkCodes: []int{200},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete will permanently delete a particular floating IP resource. Please
 | 
			
		||||
// ensure this is what you want - you can also disassociate the IP from existing
 | 
			
		||||
// internal ports.
 | 
			
		||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
@@ -1,127 +0,0 @@
 | 
			
		||||
package floatingips
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FloatingIP represents a floating IP resource. A floating IP is an external
 | 
			
		||||
// IP address that is mapped to an internal port and, optionally, a specific
 | 
			
		||||
// IP address on a private network. In other words, it enables access to an
 | 
			
		||||
// instance on a private network from an external network. For this reason,
 | 
			
		||||
// floating IPs can only be defined on networks where the `router:external'
 | 
			
		||||
// attribute (provided by the external network extension) is set to True.
 | 
			
		||||
type FloatingIP struct {
 | 
			
		||||
	// Unique identifier for the floating IP instance.
 | 
			
		||||
	ID string `json:"id" mapstructure:"id"`
 | 
			
		||||
 | 
			
		||||
	// UUID of the external network where the floating IP is to be created.
 | 
			
		||||
	FloatingNetworkID string `json:"floating_network_id" mapstructure:"floating_network_id"`
 | 
			
		||||
 | 
			
		||||
	// Address of the floating IP on the external network.
 | 
			
		||||
	FloatingIP string `json:"floating_ip_address" mapstructure:"floating_ip_address"`
 | 
			
		||||
 | 
			
		||||
	// UUID of the port on an internal network that is associated with the floating IP.
 | 
			
		||||
	PortID string `json:"port_id" mapstructure:"port_id"`
 | 
			
		||||
 | 
			
		||||
	// The specific IP address of the internal port which should be associated
 | 
			
		||||
	// with the floating IP.
 | 
			
		||||
	FixedIP string `json:"fixed_ip_address" mapstructure:"fixed_ip_address"`
 | 
			
		||||
 | 
			
		||||
	// Owner of the floating IP. Only admin users can specify a tenant identifier
 | 
			
		||||
	// other than its own.
 | 
			
		||||
	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
 | 
			
		||||
 | 
			
		||||
	// The condition of the API resource.
 | 
			
		||||
	Status string `json:"status" mapstructure:"status"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract a result and extracts a FloatingIP resource.
 | 
			
		||||
func (r commonResult) Extract() (*FloatingIP, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		FloatingIP *FloatingIP `json:"floatingip"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Error decoding Neutron floating IP: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return res.FloatingIP, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 an update operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FloatingIPPage is the page returned by a pager when traversing over a
 | 
			
		||||
// collection of floating IPs.
 | 
			
		||||
type FloatingIPPage struct {
 | 
			
		||||
	pagination.LinkedPageBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextPageURL is invoked when a paginated collection of floating IPs has reached
 | 
			
		||||
// the end of a page and the pager seeks to traverse over a new one. In order
 | 
			
		||||
// to do this, it needs to construct the next page's URL.
 | 
			
		||||
func (p FloatingIPPage) NextPageURL() (string, error) {
 | 
			
		||||
	type resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"floatingips_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a NetworkPage struct is empty.
 | 
			
		||||
func (p FloatingIPPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractFloatingIPs(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractFloatingIPs accepts a Page struct, specifically a FloatingIPPage struct,
 | 
			
		||||
// and extracts the elements into a slice of FloatingIP structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractFloatingIPs(page pagination.Page) ([]FloatingIP, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		FloatingIPs []FloatingIP `mapstructure:"floatingips" json:"floatingips"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(FloatingIPPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.FloatingIPs, err
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
package floatingips
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const resourcePath = "floatingips"
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(resourcePath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(resourcePath, id)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,256 +0,0 @@
 | 
			
		||||
package routers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"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 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"`
 | 
			
		||||
	Distributed  *bool  `q:"distributed"`
 | 
			
		||||
	Status       string `q:"status"`
 | 
			
		||||
	TenantID     string `q:"tenant_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
 | 
			
		||||
// 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 RouterPage{pagination.LinkedPageBase{PageResult: r}}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateOptsBuilder is the interface options structs have to satisfy in order
 | 
			
		||||
// to be used in the main Create operation in this package. Since many
 | 
			
		||||
// extensions decorate or modify the common logic, it is useful for them to
 | 
			
		||||
// satisfy a basic interface in order for them to be used.
 | 
			
		||||
type CreateOptsBuilder interface {
 | 
			
		||||
	ToRouterCreateMap() (map[string]interface{}, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateOpts contains all the values needed to create a new router. There are
 | 
			
		||||
// no required values.
 | 
			
		||||
type CreateOpts struct {
 | 
			
		||||
	Name         string
 | 
			
		||||
	AdminStateUp *bool
 | 
			
		||||
	Distributed  *bool
 | 
			
		||||
	TenantID     string
 | 
			
		||||
	GatewayInfo  *GatewayInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToRouterCreateMap casts a CreateOpts struct to a map.
 | 
			
		||||
func (opts CreateOpts) ToRouterCreateMap() (map[string]interface{}, error) {
 | 
			
		||||
	r := make(map[string]interface{})
 | 
			
		||||
 | 
			
		||||
	if gophercloud.MaybeString(opts.Name) != nil {
 | 
			
		||||
		r["name"] = opts.Name
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.AdminStateUp != nil {
 | 
			
		||||
		r["admin_state_up"] = opts.AdminStateUp
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.Distributed != nil {
 | 
			
		||||
		r["distributed"] = opts.Distributed
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if gophercloud.MaybeString(opts.TenantID) != nil {
 | 
			
		||||
		r["tenant_id"] = opts.TenantID
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.GatewayInfo != nil {
 | 
			
		||||
		r["external_gateway_info"] = opts.GatewayInfo
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return map[string]interface{}{"router": r}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create accepts a CreateOpts struct and uses the values to create a new
 | 
			
		||||
// logical router. When it is created, the router does not have an internal
 | 
			
		||||
// interface - it is not associated to any subnet.
 | 
			
		||||
//
 | 
			
		||||
// You can optionally specify an external gateway for a router using the
 | 
			
		||||
// GatewayInfo struct. The external gateway for the router must be plugged into
 | 
			
		||||
// an external network (it is external if its `router:external' field is set to
 | 
			
		||||
// true).
 | 
			
		||||
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
 | 
			
		||||
	var res CreateResult
 | 
			
		||||
 | 
			
		||||
	reqBody, err := opts.ToRouterCreateMap()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		res.Err = err
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular router based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateOpts contains the values used when updating a router.
 | 
			
		||||
type UpdateOpts struct {
 | 
			
		||||
	Name         string
 | 
			
		||||
	AdminStateUp *bool
 | 
			
		||||
	Distributed  *bool
 | 
			
		||||
	GatewayInfo  *GatewayInfo
 | 
			
		||||
	Routes       []Route
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update allows routers to be updated. You can update the name, administrative
 | 
			
		||||
// state, and the external gateway. For more information about how to set the
 | 
			
		||||
// external gateway for a router, see Create. This operation does not enable
 | 
			
		||||
// the update of router interfaces. To do this, use the AddInterface and
 | 
			
		||||
// RemoveInterface functions.
 | 
			
		||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
 | 
			
		||||
	type router struct {
 | 
			
		||||
		Name         *string      `json:"name,omitempty"`
 | 
			
		||||
		AdminStateUp *bool        `json:"admin_state_up,omitempty"`
 | 
			
		||||
		Distributed  *bool        `json:"distributed,omitempty"`
 | 
			
		||||
		GatewayInfo  *GatewayInfo `json:"external_gateway_info,omitempty"`
 | 
			
		||||
		Routes       []Route      `json:"routes"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type request struct {
 | 
			
		||||
		Router router `json:"router"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{Router: router{
 | 
			
		||||
		Name:         gophercloud.MaybeString(opts.Name),
 | 
			
		||||
		AdminStateUp: opts.AdminStateUp,
 | 
			
		||||
		Distributed:  opts.Distributed,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	if opts.GatewayInfo != nil {
 | 
			
		||||
		reqBody.Router.GatewayInfo = opts.GatewayInfo
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.Routes != nil {
 | 
			
		||||
		reqBody.Router.Routes = opts.Routes
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Send request to API
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
 | 
			
		||||
		OkCodes: []int{200},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete will permanently delete a particular router based on its unique ID.
 | 
			
		||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errInvalidInterfaceOpts = errors.New("When adding a router interface you must provide either a subnet ID or a port ID")
 | 
			
		||||
 | 
			
		||||
// InterfaceOpts allow you to work with operations that either add or remote
 | 
			
		||||
// an internal interface from a router.
 | 
			
		||||
type InterfaceOpts struct {
 | 
			
		||||
	SubnetID string
 | 
			
		||||
	PortID   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddInterface attaches a subnet to an internal router interface. You must
 | 
			
		||||
// specify either a SubnetID or PortID in the request body. If you specify both,
 | 
			
		||||
// the operation will fail and an error will be returned.
 | 
			
		||||
//
 | 
			
		||||
// If you specify a SubnetID, the gateway IP address for that particular subnet
 | 
			
		||||
// is used to create the router interface. Alternatively, if you specify a
 | 
			
		||||
// PortID, the IP address associated with the port is used to create the router
 | 
			
		||||
// interface.
 | 
			
		||||
//
 | 
			
		||||
// If you reference a port that is associated with multiple IP addresses, or
 | 
			
		||||
// if the port is associated with zero IP addresses, the operation will fail and
 | 
			
		||||
// a 400 Bad Request error will be returned.
 | 
			
		||||
//
 | 
			
		||||
// If you reference a port already in use, the operation will fail and a 409
 | 
			
		||||
// Conflict error will be returned.
 | 
			
		||||
//
 | 
			
		||||
// The PortID that is returned after using Extract() on the result of this
 | 
			
		||||
// operation can either be the same PortID passed in or, on the other hand, the
 | 
			
		||||
// identifier of a new port created by this operation. After the operation
 | 
			
		||||
// completes, the device ID of the port is set to the router ID, and the
 | 
			
		||||
// device owner attribute is set to `network:router_interface'.
 | 
			
		||||
func AddInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOpts) InterfaceResult {
 | 
			
		||||
	var res InterfaceResult
 | 
			
		||||
 | 
			
		||||
	// Validate
 | 
			
		||||
	if (opts.SubnetID == "" && opts.PortID == "") || (opts.SubnetID != "" && opts.PortID != "") {
 | 
			
		||||
		res.Err = errInvalidInterfaceOpts
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type request struct {
 | 
			
		||||
		SubnetID string `json:"subnet_id,omitempty"`
 | 
			
		||||
		PortID   string `json:"port_id,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	body := request{SubnetID: opts.SubnetID, PortID: opts.PortID}
 | 
			
		||||
 | 
			
		||||
	_, res.Err = c.Put(addInterfaceURL(c, id), body, &res.Body, &gophercloud.RequestOpts{
 | 
			
		||||
		OkCodes: []int{200},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveInterface removes an internal router interface, which detaches a
 | 
			
		||||
// subnet from the router. You must specify either a SubnetID or PortID, since
 | 
			
		||||
// these values are used to identify the router interface to remove.
 | 
			
		||||
//
 | 
			
		||||
// Unlike AddInterface, you can also specify both a SubnetID and PortID. If you
 | 
			
		||||
// choose to specify both, the subnet ID must correspond to the subnet ID of
 | 
			
		||||
// the first IP address on the port specified by the port ID. Otherwise, the
 | 
			
		||||
// operation will fail and return a 409 Conflict error.
 | 
			
		||||
//
 | 
			
		||||
// If the router, subnet or port which are referenced do not exist or are not
 | 
			
		||||
// visible to you, the operation will fail and a 404 Not Found error will be
 | 
			
		||||
// returned. After this operation completes, the port connecting the router
 | 
			
		||||
// with the subnet is removed from the subnet for the network.
 | 
			
		||||
func RemoveInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOpts) InterfaceResult {
 | 
			
		||||
	var res InterfaceResult
 | 
			
		||||
 | 
			
		||||
	type request struct {
 | 
			
		||||
		SubnetID string `json:"subnet_id,omitempty"`
 | 
			
		||||
		PortID   string `json:"port_id,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	body := request{SubnetID: opts.SubnetID, PortID: opts.PortID}
 | 
			
		||||
 | 
			
		||||
	_, res.Err = c.Put(removeInterfaceURL(c, id), body, &res.Body, &gophercloud.RequestOpts{
 | 
			
		||||
		OkCodes: []int{200},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
@@ -1,171 +0,0 @@
 | 
			
		||||
package routers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GatewayInfo represents the information of an external gateway for any
 | 
			
		||||
// particular network router.
 | 
			
		||||
type GatewayInfo struct {
 | 
			
		||||
	NetworkID string `json:"network_id" mapstructure:"network_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Route struct {
 | 
			
		||||
	NextHop         string `mapstructure:"nexthop" json:"nexthop"`
 | 
			
		||||
	DestinationCIDR string `mapstructure:"destination" json:"destination"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Router represents a Neutron router. A router is a logical entity that
 | 
			
		||||
// forwards packets across internal subnets and NATs (network address
 | 
			
		||||
// translation) them on external networks through an appropriate gateway.
 | 
			
		||||
//
 | 
			
		||||
// A router has an interface for each subnet with which it is associated. By
 | 
			
		||||
// default, the IP address of such interface is the subnet's gateway IP. Also,
 | 
			
		||||
// whenever a router is associated with a subnet, a port for that router
 | 
			
		||||
// interface is added to the subnet's network.
 | 
			
		||||
type Router struct {
 | 
			
		||||
	// Indicates whether or not a router is currently operational.
 | 
			
		||||
	Status string `json:"status" mapstructure:"status"`
 | 
			
		||||
 | 
			
		||||
	// Information on external gateway for the router.
 | 
			
		||||
	GatewayInfo GatewayInfo `json:"external_gateway_info" mapstructure:"external_gateway_info"`
 | 
			
		||||
 | 
			
		||||
	// Administrative state of the router.
 | 
			
		||||
	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
 | 
			
		||||
 | 
			
		||||
	// Whether router is disitrubted or not..
 | 
			
		||||
	Distributed bool `json:"distributed" mapstructure:"distributed"`
 | 
			
		||||
 | 
			
		||||
	// Human readable name for the router. Does not have to be unique.
 | 
			
		||||
	Name string `json:"name" mapstructure:"name"`
 | 
			
		||||
 | 
			
		||||
	// Unique identifier for the router.
 | 
			
		||||
	ID string `json:"id" mapstructure:"id"`
 | 
			
		||||
 | 
			
		||||
	// Owner of the router. Only admin users can specify a tenant identifier
 | 
			
		||||
	// other than its own.
 | 
			
		||||
	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
 | 
			
		||||
 | 
			
		||||
	Routes []Route `json:"routes" mapstructure:"routes"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RouterPage is the page returned by a pager when traversing over a
 | 
			
		||||
// collection of routers.
 | 
			
		||||
type RouterPage struct {
 | 
			
		||||
	pagination.LinkedPageBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextPageURL is invoked when a paginated collection of routers has reached
 | 
			
		||||
// the end of a page and the pager seeks to traverse over a new one. In order
 | 
			
		||||
// to do this, it needs to construct the next page's URL.
 | 
			
		||||
func (p RouterPage) NextPageURL() (string, error) {
 | 
			
		||||
	type resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"routers_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a RouterPage struct is empty.
 | 
			
		||||
func (p RouterPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractRouters(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractRouters accepts a Page struct, specifically a RouterPage struct,
 | 
			
		||||
// and extracts the elements into a slice of Router structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractRouters(page pagination.Page) ([]Router, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		Routers []Router `mapstructure:"routers" json:"routers"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(RouterPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.Routers, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract is a function that accepts a result and extracts a router.
 | 
			
		||||
func (r commonResult) Extract() (*Router, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		Router *Router `json:"router"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.Router, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateResult represents the result of a create operation.
 | 
			
		||||
type CreateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateResult represents the result of an update operation.
 | 
			
		||||
type UpdateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InterfaceInfo represents information about a particular router interface. As
 | 
			
		||||
// mentioned above, in order for a router to forward to a subnet, it needs an
 | 
			
		||||
// interface.
 | 
			
		||||
type InterfaceInfo struct {
 | 
			
		||||
	// The ID of the subnet which this interface is associated with.
 | 
			
		||||
	SubnetID string `json:"subnet_id" mapstructure:"subnet_id"`
 | 
			
		||||
 | 
			
		||||
	// The ID of the port that is a part of the subnet.
 | 
			
		||||
	PortID string `json:"port_id" mapstructure:"port_id"`
 | 
			
		||||
 | 
			
		||||
	// The UUID of the interface.
 | 
			
		||||
	ID string `json:"id" mapstructure:"id"`
 | 
			
		||||
 | 
			
		||||
	// Owner of the interface.
 | 
			
		||||
	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InterfaceResult represents the result of interface operations, such as
 | 
			
		||||
// AddInterface() and RemoveInterface().
 | 
			
		||||
type InterfaceResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract is a function that accepts a result and extracts an information struct.
 | 
			
		||||
func (r InterfaceResult) Extract() (*InterfaceInfo, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res *InterfaceInfo
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res, err
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
package routers
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const resourcePath = "routers"
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(resourcePath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(resourcePath, id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func addInterfaceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(resourcePath, id, "add_router_interface")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func removeInterfaceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(resourcePath, id, "remove_router_interface")
 | 
			
		||||
}
 | 
			
		||||
@@ -1,123 +0,0 @@
 | 
			
		||||
package members
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"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 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 `q:"status"`
 | 
			
		||||
	Weight       int    `q:"weight"`
 | 
			
		||||
	AdminStateUp *bool  `q:"admin_state_up"`
 | 
			
		||||
	TenantID     string `q:"tenant_id"`
 | 
			
		||||
	PoolID       string `q:"pool_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
 | 
			
		||||
// 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}
 | 
			
		||||
	}
 | 
			
		||||
	u := rootURL(c) + 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 pool member.
 | 
			
		||||
type CreateOpts struct {
 | 
			
		||||
	// Only required if the caller has an admin role and wants to create a pool
 | 
			
		||||
	// for another tenant.
 | 
			
		||||
	TenantID string
 | 
			
		||||
 | 
			
		||||
	// Required. The IP address of the member.
 | 
			
		||||
	Address string
 | 
			
		||||
 | 
			
		||||
	// Required. The port on which the application is hosted.
 | 
			
		||||
	ProtocolPort int
 | 
			
		||||
 | 
			
		||||
	// Required. The pool to which this member will belong.
 | 
			
		||||
	PoolID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create accepts a CreateOpts struct and uses the values to create a new
 | 
			
		||||
// load balancer pool member.
 | 
			
		||||
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 | 
			
		||||
	type member struct {
 | 
			
		||||
		TenantID     string `json:"tenant_id,omitempty"`
 | 
			
		||||
		ProtocolPort int    `json:"protocol_port"`
 | 
			
		||||
		Address      string `json:"address"`
 | 
			
		||||
		PoolID       string `json:"pool_id"`
 | 
			
		||||
	}
 | 
			
		||||
	type request struct {
 | 
			
		||||
		Member member `json:"member"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{Member: member{
 | 
			
		||||
		Address:      opts.Address,
 | 
			
		||||
		TenantID:     opts.TenantID,
 | 
			
		||||
		ProtocolPort: opts.ProtocolPort,
 | 
			
		||||
		PoolID:       opts.PoolID,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	var res CreateResult
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular pool member based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateOpts contains the values used when updating a pool member.
 | 
			
		||||
type UpdateOpts struct {
 | 
			
		||||
	// The administrative state of the member, which is up (true) or down (false).
 | 
			
		||||
	AdminStateUp bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update allows members to be updated.
 | 
			
		||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
 | 
			
		||||
	type member struct {
 | 
			
		||||
		AdminStateUp bool `json:"admin_state_up"`
 | 
			
		||||
	}
 | 
			
		||||
	type request struct {
 | 
			
		||||
		Member member `json:"member"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{Member: member{AdminStateUp: opts.AdminStateUp}}
 | 
			
		||||
 | 
			
		||||
	// Send request to API
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
 | 
			
		||||
		OkCodes: []int{200, 201, 202},
 | 
			
		||||
	})
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete will permanently delete a particular member based on its unique ID.
 | 
			
		||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
@@ -1,122 +0,0 @@
 | 
			
		||||
package members
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Member represents the application running on a backend server.
 | 
			
		||||
type Member struct {
 | 
			
		||||
	// The status of the member. Indicates whether the member is operational.
 | 
			
		||||
	Status string
 | 
			
		||||
 | 
			
		||||
	// Weight of member.
 | 
			
		||||
	Weight int
 | 
			
		||||
 | 
			
		||||
	// The administrative state of the member, which is up (true) or down (false).
 | 
			
		||||
	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
 | 
			
		||||
 | 
			
		||||
	// Owner of the member. Only an administrative user can specify a tenant ID
 | 
			
		||||
	// other than its own.
 | 
			
		||||
	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
 | 
			
		||||
 | 
			
		||||
	// The pool to which the member belongs.
 | 
			
		||||
	PoolID string `json:"pool_id" mapstructure:"pool_id"`
 | 
			
		||||
 | 
			
		||||
	// The IP address of the member.
 | 
			
		||||
	Address string
 | 
			
		||||
 | 
			
		||||
	// The port on which the application is hosted.
 | 
			
		||||
	ProtocolPort int `json:"protocol_port" mapstructure:"protocol_port"`
 | 
			
		||||
 | 
			
		||||
	// The unique ID for the member.
 | 
			
		||||
	ID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MemberPage is the page returned by a pager when traversing over a
 | 
			
		||||
// collection of pool members.
 | 
			
		||||
type MemberPage struct {
 | 
			
		||||
	pagination.LinkedPageBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextPageURL is invoked when a paginated collection of members has reached
 | 
			
		||||
// the end of a page and the pager seeks to traverse over a new one. In order
 | 
			
		||||
// to do this, it needs to construct the next page's URL.
 | 
			
		||||
func (p MemberPage) NextPageURL() (string, error) {
 | 
			
		||||
	type resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"members_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a MemberPage struct is empty.
 | 
			
		||||
func (p MemberPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractMembers(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractMembers accepts a Page struct, specifically a MemberPage struct,
 | 
			
		||||
// and extracts the elements into a slice of Member structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractMembers(page pagination.Page) ([]Member, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		Members []Member `mapstructure:"members" json:"members"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(MemberPage).Body, &resp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return resp.Members, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract is a function that accepts a result and extracts a router.
 | 
			
		||||
func (r commonResult) Extract() (*Member, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		Member *Member `json:"member"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.Member, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateResult represents the result of a create operation.
 | 
			
		||||
type CreateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateResult represents the result of an update operation.
 | 
			
		||||
type UpdateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
package members
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rootPath     = "lb"
 | 
			
		||||
	resourcePath = "members"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath, id)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,265 +0,0 @@
 | 
			
		||||
package monitors
 | 
			
		||||
 | 
			
		||||
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 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"`
 | 
			
		||||
	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 (
 | 
			
		||||
	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 {
 | 
			
		||||
	// 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 opts.Delay < opts.Timeout {
 | 
			
		||||
		res.Err = errDelayMustGETimeout
 | 
			
		||||
	}
 | 
			
		||||
	if res.Err != nil {
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type monitor struct {
 | 
			
		||||
		Type          string  `json:"type"`
 | 
			
		||||
		Delay         int     `json:"delay"`
 | 
			
		||||
		Timeout       int     `json:"timeout"`
 | 
			
		||||
		MaxRetries    int     `json:"max_retries"`
 | 
			
		||||
		TenantID      *string `json:"tenant_id,omitempty"`
 | 
			
		||||
		URLPath       *string `json:"url_path,omitempty"`
 | 
			
		||||
		ExpectedCodes *string `json:"expected_codes,omitempty"`
 | 
			
		||||
		HTTPMethod    *string `json:"http_method,omitempty"`
 | 
			
		||||
		AdminStateUp  *bool   `json:"admin_state_up,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type request struct {
 | 
			
		||||
		Monitor monitor `json:"health_monitor"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{Monitor: monitor{
 | 
			
		||||
		Type:          opts.Type,
 | 
			
		||||
		Delay:         opts.Delay,
 | 
			
		||||
		Timeout:       opts.Timeout,
 | 
			
		||||
		MaxRetries:    opts.MaxRetries,
 | 
			
		||||
		TenantID:      gophercloud.MaybeString(opts.TenantID),
 | 
			
		||||
		URLPath:       gophercloud.MaybeString(opts.URLPath),
 | 
			
		||||
		ExpectedCodes: gophercloud.MaybeString(opts.ExpectedCodes),
 | 
			
		||||
		HTTPMethod:    gophercloud.MaybeString(opts.HTTPMethod),
 | 
			
		||||
		AdminStateUp:  opts.AdminStateUp,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular health monitor based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
 | 
			
		||||
	if opts.Delay > 0 && opts.Timeout > 0 && opts.Delay < opts.Timeout {
 | 
			
		||||
		res.Err = errDelayMustGETimeout
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type monitor struct {
 | 
			
		||||
		Delay         int     `json:"delay"`
 | 
			
		||||
		Timeout       int     `json:"timeout"`
 | 
			
		||||
		MaxRetries    int     `json:"max_retries"`
 | 
			
		||||
		URLPath       *string `json:"url_path,omitempty"`
 | 
			
		||||
		ExpectedCodes *string `json:"expected_codes,omitempty"`
 | 
			
		||||
		HTTPMethod    *string `json:"http_method,omitempty"`
 | 
			
		||||
		AdminStateUp  *bool   `json:"admin_state_up,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type request struct {
 | 
			
		||||
		Monitor monitor `json:"health_monitor"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{Monitor: monitor{
 | 
			
		||||
		Delay:         opts.Delay,
 | 
			
		||||
		Timeout:       opts.Timeout,
 | 
			
		||||
		MaxRetries:    opts.MaxRetries,
 | 
			
		||||
		URLPath:       gophercloud.MaybeString(opts.URLPath),
 | 
			
		||||
		ExpectedCodes: gophercloud.MaybeString(opts.ExpectedCodes),
 | 
			
		||||
		HTTPMethod:    gophercloud.MaybeString(opts.HTTPMethod),
 | 
			
		||||
		AdminStateUp:  opts.AdminStateUp,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
 | 
			
		||||
		OkCodes: []int{200, 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
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
@@ -1,150 +0,0 @@
 | 
			
		||||
package monitors
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"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
 | 
			
		||||
 | 
			
		||||
	// 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"`
 | 
			
		||||
 | 
			
		||||
	// 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 resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"health_monitors_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a PoolPage struct is empty.
 | 
			
		||||
func (p MonitorPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractMonitors(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractMonitors accepts a Page struct, specifically a MonitorPage struct,
 | 
			
		||||
// and extracts the elements into a slice of Monitor structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractMonitors(page pagination.Page) ([]Monitor, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		Monitors []Monitor `mapstructure:"health_monitors" json:"health_monitors"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(MonitorPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.Monitors, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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:"health_monitor" mapstructure:"health_monitor"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.Monitor, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateResult represents the result of a create operation.
 | 
			
		||||
type CreateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateResult represents the result of an update operation.
 | 
			
		||||
type UpdateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
package monitors
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rootPath     = "lb"
 | 
			
		||||
	resourcePath = "health_monitors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath, id)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,186 +0,0 @@
 | 
			
		||||
package pools
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"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 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 `q:"status"`
 | 
			
		||||
	LBMethod     string `q:"lb_method"`
 | 
			
		||||
	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"`
 | 
			
		||||
	VIPID        string `q:"vip_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
 | 
			
		||||
// 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}
 | 
			
		||||
	}
 | 
			
		||||
	u := rootURL(c) + q.String()
 | 
			
		||||
	return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
 | 
			
		||||
		return PoolPage{pagination.LinkedPageBase{PageResult: r}}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Supported attributes for create/update operations.
 | 
			
		||||
const (
 | 
			
		||||
	LBMethodRoundRobin       = "ROUND_ROBIN"
 | 
			
		||||
	LBMethodLeastConnections = "LEAST_CONNECTIONS"
 | 
			
		||||
 | 
			
		||||
	ProtocolTCP   = "TCP"
 | 
			
		||||
	ProtocolHTTP  = "HTTP"
 | 
			
		||||
	ProtocolHTTPS = "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
 | 
			
		||||
 | 
			
		||||
	// Required. Name of the pool.
 | 
			
		||||
	Name string
 | 
			
		||||
 | 
			
		||||
	// Required. The protocol used by the pool members, you can use either
 | 
			
		||||
	// ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS.
 | 
			
		||||
	Protocol string
 | 
			
		||||
 | 
			
		||||
	// 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
 | 
			
		||||
 | 
			
		||||
	// The algorithm used to distribute load between the members of the pool. The
 | 
			
		||||
	// current specification supports LBMethodRoundRobin and
 | 
			
		||||
	// LBMethodLeastConnections as valid values for this attribute.
 | 
			
		||||
	LBMethod string
 | 
			
		||||
 | 
			
		||||
	// The provider of the pool
 | 
			
		||||
	Provider string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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"`
 | 
			
		||||
		TenantID string `json:"tenant_id,omitempty"`
 | 
			
		||||
		Protocol string `json:"protocol"`
 | 
			
		||||
		SubnetID string `json:"subnet_id"`
 | 
			
		||||
		LBMethod string `json:"lb_method"`
 | 
			
		||||
		Provider string `json:"provider,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
	type request struct {
 | 
			
		||||
		Pool pool `json:"pool"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{Pool: pool{
 | 
			
		||||
		Name:     opts.Name,
 | 
			
		||||
		TenantID: opts.TenantID,
 | 
			
		||||
		Protocol: opts.Protocol,
 | 
			
		||||
		SubnetID: opts.SubnetID,
 | 
			
		||||
		LBMethod: opts.LBMethod,
 | 
			
		||||
		Provider: opts.Provider,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	var res CreateResult
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular pool based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateOpts contains the values used when updating a pool.
 | 
			
		||||
type UpdateOpts struct {
 | 
			
		||||
	// Required. Name of the pool.
 | 
			
		||||
	Name string
 | 
			
		||||
 | 
			
		||||
	// The algorithm used to distribute load between the members of the pool. The
 | 
			
		||||
	// current specification supports LBMethodRoundRobin and
 | 
			
		||||
	// LBMethodLeastConnections as valid values for this attribute.
 | 
			
		||||
	LBMethod string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update allows pools to be updated.
 | 
			
		||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
 | 
			
		||||
	type pool struct {
 | 
			
		||||
		Name     string `json:"name,"`
 | 
			
		||||
		LBMethod string `json:"lb_method"`
 | 
			
		||||
	}
 | 
			
		||||
	type request struct {
 | 
			
		||||
		Pool pool `json:"pool"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{Pool: pool{
 | 
			
		||||
		Name:     opts.Name,
 | 
			
		||||
		LBMethod: opts.LBMethod,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	// Send request to API
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
 | 
			
		||||
		OkCodes: []int{200},
 | 
			
		||||
	})
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete will permanently delete a particular pool based on its unique ID.
 | 
			
		||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AssociateMonitor will associate a health monitor with a particular pool.
 | 
			
		||||
// Once associated, the health monitor will start monitoring the members of the
 | 
			
		||||
// pool and will deactivate these members if they are deemed unhealthy. A
 | 
			
		||||
// member can be deactivated (status set to INACTIVE) if any of health monitors
 | 
			
		||||
// finds it unhealthy.
 | 
			
		||||
func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) AssociateResult {
 | 
			
		||||
	type hm struct {
 | 
			
		||||
		ID string `json:"id"`
 | 
			
		||||
	}
 | 
			
		||||
	type request struct {
 | 
			
		||||
		Monitor hm `json:"health_monitor"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{hm{ID: monitorID}}
 | 
			
		||||
 | 
			
		||||
	var res AssociateResult
 | 
			
		||||
	_, res.Err = c.Post(associateURL(c, poolID), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DisassociateMonitor will disassociate a health monitor with a particular
 | 
			
		||||
// pool. When dissociation is successful, the health monitor will no longer
 | 
			
		||||
// check for the health of the members of the pool.
 | 
			
		||||
func DisassociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) AssociateResult {
 | 
			
		||||
	var res AssociateResult
 | 
			
		||||
	_, res.Err = c.Delete(disassociateURL(c, poolID, monitorID), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
@@ -1,146 +0,0 @@
 | 
			
		||||
package pools
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
// method to handle the new requests or connections received on the VIP address.
 | 
			
		||||
// There is only one pool per virtual IP.
 | 
			
		||||
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 network on which the members of the pool will be located. Only members
 | 
			
		||||
	// that are on this network can be added to the pool.
 | 
			
		||||
	SubnetID string `json:"subnet_id" mapstructure:"subnet_id"`
 | 
			
		||||
 | 
			
		||||
	// 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"`
 | 
			
		||||
 | 
			
		||||
	// The provider
 | 
			
		||||
	Provider string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"pools_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a PoolPage struct is empty.
 | 
			
		||||
func (p PoolPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractPools(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractPools accepts a Page struct, specifically a RouterPage struct,
 | 
			
		||||
// and extracts the elements into a slice of Router structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractPools(page pagination.Page) ([]Pool, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		Pools []Pool `mapstructure:"pools" json:"pools"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(PoolPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.Pools, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract is a function that accepts a result and extracts a router.
 | 
			
		||||
func (r commonResult) Extract() (*Pool, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		Pool *Pool `json:"pool"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.Pool, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateResult represents the result of a create operation.
 | 
			
		||||
type CreateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateResult represents the result of an update operation.
 | 
			
		||||
type UpdateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AssociateResult represents the result of an association operation.
 | 
			
		||||
type AssociateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
@@ -1,25 +0,0 @@
 | 
			
		||||
package pools
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rootPath     = "lb"
 | 
			
		||||
	resourcePath = "pools"
 | 
			
		||||
	monitorPath  = "health_monitors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath, id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func associateURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath, id, monitorPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func disassociateURL(c *gophercloud.ServiceClient, poolID, monitorID string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath, poolID, monitorPath, monitorID)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,256 +0,0 @@
 | 
			
		||||
package vips
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AdminState gives users a solid type to work with for create and update
 | 
			
		||||
// operations. It is recommended that users use the `Up` and `Down` enums.
 | 
			
		||||
type AdminState *bool
 | 
			
		||||
 | 
			
		||||
// Convenience vars for AdminStateUp values.
 | 
			
		||||
var (
 | 
			
		||||
	iTrue  = true
 | 
			
		||||
	iFalse = false
 | 
			
		||||
 | 
			
		||||
	Up   AdminState = &iTrue
 | 
			
		||||
	Down AdminState = &iFalse
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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"`
 | 
			
		||||
	Status          string `q:"status"`
 | 
			
		||||
	TenantID        string `q:"tenant_id"`
 | 
			
		||||
	SubnetID        string `q:"subnet_id"`
 | 
			
		||||
	Address         string `q:"address"`
 | 
			
		||||
	PortID          string `q:"port_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 VIPPage{pagination.LinkedPageBase{PageResult: r}}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	errNameRequired         = fmt.Errorf("Name is required")
 | 
			
		||||
	errSubnetIDRequried     = fmt.Errorf("SubnetID is required")
 | 
			
		||||
	errProtocolRequired     = fmt.Errorf("Protocol is required")
 | 
			
		||||
	errProtocolPortRequired = fmt.Errorf("Protocol port is required")
 | 
			
		||||
	errPoolIDRequired       = fmt.Errorf("PoolID is required")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CreateOpts contains all the values needed to create a new virtual IP.
 | 
			
		||||
type CreateOpts struct {
 | 
			
		||||
	// Required. Human-readable name for the VIP. Does not have to be unique.
 | 
			
		||||
	Name string
 | 
			
		||||
 | 
			
		||||
	// Required. The network on which to allocate the VIP's address. A tenant can
 | 
			
		||||
	// only create VIPs on networks authorized by policy (e.g. networks that
 | 
			
		||||
	// belong to them or networks that are shared).
 | 
			
		||||
	SubnetID string
 | 
			
		||||
 | 
			
		||||
	// Required. The protocol - can either be TCP, HTTP or HTTPS.
 | 
			
		||||
	Protocol string
 | 
			
		||||
 | 
			
		||||
	// Required. The port on which to listen for client traffic.
 | 
			
		||||
	ProtocolPort int
 | 
			
		||||
 | 
			
		||||
	// Required. The ID of the pool with which the VIP is associated.
 | 
			
		||||
	PoolID string
 | 
			
		||||
 | 
			
		||||
	// Required for admins. Indicates the owner of the VIP.
 | 
			
		||||
	TenantID string
 | 
			
		||||
 | 
			
		||||
	// Optional. The IP address of the VIP.
 | 
			
		||||
	Address string
 | 
			
		||||
 | 
			
		||||
	// Optional. Human-readable description for the VIP.
 | 
			
		||||
	Description string
 | 
			
		||||
 | 
			
		||||
	// Optional. Omit this field to prevent session persistence.
 | 
			
		||||
	Persistence *SessionPersistence
 | 
			
		||||
 | 
			
		||||
	// Optional. The maximum number of connections allowed for the VIP.
 | 
			
		||||
	ConnLimit *int
 | 
			
		||||
 | 
			
		||||
	// Optional. The administrative state of the VIP. A valid value is true (UP)
 | 
			
		||||
	// or false (DOWN).
 | 
			
		||||
	AdminStateUp *bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create is an operation which provisions a new virtual IP based on the
 | 
			
		||||
// configuration defined in the CreateOpts struct. Once the request is
 | 
			
		||||
// validated and progress has started on the provisioning process, a
 | 
			
		||||
// CreateResult will be returned.
 | 
			
		||||
//
 | 
			
		||||
// Please note that the PoolID should refer to a pool that is not already
 | 
			
		||||
// associated with another vip. If the pool is already used by another vip,
 | 
			
		||||
// then the operation will fail with a 409 Conflict error will be returned.
 | 
			
		||||
//
 | 
			
		||||
// Users with an admin role can create VIPs on behalf of other tenants by
 | 
			
		||||
// specifying a TenantID attribute different than their own.
 | 
			
		||||
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 | 
			
		||||
	var res CreateResult
 | 
			
		||||
 | 
			
		||||
	// Validate required opts
 | 
			
		||||
	if opts.Name == "" {
 | 
			
		||||
		res.Err = errNameRequired
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
	if opts.SubnetID == "" {
 | 
			
		||||
		res.Err = errSubnetIDRequried
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Protocol == "" {
 | 
			
		||||
		res.Err = errProtocolRequired
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
	if opts.ProtocolPort == 0 {
 | 
			
		||||
		res.Err = errProtocolPortRequired
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
	if opts.PoolID == "" {
 | 
			
		||||
		res.Err = errPoolIDRequired
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type vip struct {
 | 
			
		||||
		Name         string              `json:"name"`
 | 
			
		||||
		SubnetID     string              `json:"subnet_id"`
 | 
			
		||||
		Protocol     string              `json:"protocol"`
 | 
			
		||||
		ProtocolPort int                 `json:"protocol_port"`
 | 
			
		||||
		PoolID       string              `json:"pool_id"`
 | 
			
		||||
		Description  *string             `json:"description,omitempty"`
 | 
			
		||||
		TenantID     *string             `json:"tenant_id,omitempty"`
 | 
			
		||||
		Address      *string             `json:"address,omitempty"`
 | 
			
		||||
		Persistence  *SessionPersistence `json:"session_persistence,omitempty"`
 | 
			
		||||
		ConnLimit    *int                `json:"connection_limit,omitempty"`
 | 
			
		||||
		AdminStateUp *bool               `json:"admin_state_up,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type request struct {
 | 
			
		||||
		VirtualIP vip `json:"vip"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{VirtualIP: vip{
 | 
			
		||||
		Name:         opts.Name,
 | 
			
		||||
		SubnetID:     opts.SubnetID,
 | 
			
		||||
		Protocol:     opts.Protocol,
 | 
			
		||||
		ProtocolPort: opts.ProtocolPort,
 | 
			
		||||
		PoolID:       opts.PoolID,
 | 
			
		||||
		Description:  gophercloud.MaybeString(opts.Description),
 | 
			
		||||
		TenantID:     gophercloud.MaybeString(opts.TenantID),
 | 
			
		||||
		Address:      gophercloud.MaybeString(opts.Address),
 | 
			
		||||
		ConnLimit:    opts.ConnLimit,
 | 
			
		||||
		AdminStateUp: opts.AdminStateUp,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	if opts.Persistence != nil {
 | 
			
		||||
		reqBody.VirtualIP.Persistence = opts.Persistence
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular virtual IP based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	// Human-readable name for the VIP. Does not have to be unique.
 | 
			
		||||
	Name string
 | 
			
		||||
 | 
			
		||||
	// Required. The ID of the pool with which the VIP is associated.
 | 
			
		||||
	PoolID string
 | 
			
		||||
 | 
			
		||||
	// Optional. Human-readable description for the VIP.
 | 
			
		||||
	Description string
 | 
			
		||||
 | 
			
		||||
	// Optional. Omit this field to prevent session persistence.
 | 
			
		||||
	Persistence *SessionPersistence
 | 
			
		||||
 | 
			
		||||
	// Optional. The maximum number of connections allowed for the VIP.
 | 
			
		||||
	ConnLimit *int
 | 
			
		||||
 | 
			
		||||
	// Optional. The administrative state of the VIP. A valid value is true (UP)
 | 
			
		||||
	// or false (DOWN).
 | 
			
		||||
	AdminStateUp *bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update is an operation which modifies the attributes of the specified VIP.
 | 
			
		||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
 | 
			
		||||
	type vip struct {
 | 
			
		||||
		Name         string              `json:"name,omitempty"`
 | 
			
		||||
		PoolID       string              `json:"pool_id,omitempty"`
 | 
			
		||||
		Description  *string             `json:"description,omitempty"`
 | 
			
		||||
		Persistence  *SessionPersistence `json:"session_persistence,omitempty"`
 | 
			
		||||
		ConnLimit    *int                `json:"connection_limit,omitempty"`
 | 
			
		||||
		AdminStateUp *bool               `json:"admin_state_up,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type request struct {
 | 
			
		||||
		VirtualIP vip `json:"vip"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{VirtualIP: vip{
 | 
			
		||||
		Name:         opts.Name,
 | 
			
		||||
		PoolID:       opts.PoolID,
 | 
			
		||||
		Description:  gophercloud.MaybeString(opts.Description),
 | 
			
		||||
		ConnLimit:    opts.ConnLimit,
 | 
			
		||||
		AdminStateUp: opts.AdminStateUp,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	if opts.Persistence != nil {
 | 
			
		||||
		reqBody.VirtualIP.Persistence = opts.Persistence
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
 | 
			
		||||
		OkCodes: []int{200, 202},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete will permanently delete a particular virtual IP based on its unique ID.
 | 
			
		||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
@@ -1,166 +0,0 @@
 | 
			
		||||
package vips
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SessionPersistence represents the session persistence feature of the load
 | 
			
		||||
// balancing service. It attempts to force connections or requests in the same
 | 
			
		||||
// session to be processed by the same member as long as it is ative. Three
 | 
			
		||||
// types of persistence are supported:
 | 
			
		||||
//
 | 
			
		||||
// SOURCE_IP:   With this mode, all connections originating from the same source
 | 
			
		||||
//              IP address, will be handled by the same member of the pool.
 | 
			
		||||
// HTTP_COOKIE: With this persistence mode, the load balancing function will
 | 
			
		||||
//              create a cookie on the first request from a client. Subsequent
 | 
			
		||||
//              requests containing the same cookie value will be handled by
 | 
			
		||||
//              the same member of the pool.
 | 
			
		||||
// APP_COOKIE:  With this persistence mode, the load balancing function will
 | 
			
		||||
//              rely on a cookie established by the backend application. All
 | 
			
		||||
//              requests carrying the same cookie value will be handled by the
 | 
			
		||||
//              same member of the pool.
 | 
			
		||||
type SessionPersistence struct {
 | 
			
		||||
	// The type of persistence mode
 | 
			
		||||
	Type string `mapstructure:"type" json:"type"`
 | 
			
		||||
 | 
			
		||||
	// Name of cookie if persistence mode is set appropriately
 | 
			
		||||
	CookieName string `mapstructure:"cookie_name" json:"cookie_name,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VirtualIP is the primary load balancing configuration object that specifies
 | 
			
		||||
// the virtual IP address and port 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 VirtualIP struct {
 | 
			
		||||
	// The unique ID for the VIP.
 | 
			
		||||
	ID string `mapstructure:"id" json:"id"`
 | 
			
		||||
 | 
			
		||||
	// Owner of the VIP. Only an admin user can specify a tenant ID other than its own.
 | 
			
		||||
	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
 | 
			
		||||
 | 
			
		||||
	// Human-readable name for the VIP. Does not have to be unique.
 | 
			
		||||
	Name string `mapstructure:"name" json:"name"`
 | 
			
		||||
 | 
			
		||||
	// Human-readable description for the VIP.
 | 
			
		||||
	Description string `mapstructure:"description" json:"description"`
 | 
			
		||||
 | 
			
		||||
	// The ID of the subnet on which to allocate the VIP address.
 | 
			
		||||
	SubnetID string `mapstructure:"subnet_id" json:"subnet_id"`
 | 
			
		||||
 | 
			
		||||
	// The IP address of the VIP.
 | 
			
		||||
	Address string `mapstructure:"address" json:"address"`
 | 
			
		||||
 | 
			
		||||
	// The protocol of the VIP address. A valid value is TCP, HTTP, or HTTPS.
 | 
			
		||||
	Protocol string `mapstructure:"protocol" json:"protocol"`
 | 
			
		||||
 | 
			
		||||
	// The port on which to listen to client traffic that is associated with the
 | 
			
		||||
	// VIP address. A valid value is from 0 to 65535.
 | 
			
		||||
	ProtocolPort int `mapstructure:"protocol_port" json:"protocol_port"`
 | 
			
		||||
 | 
			
		||||
	// The ID of the pool with which the VIP is associated.
 | 
			
		||||
	PoolID string `mapstructure:"pool_id" json:"pool_id"`
 | 
			
		||||
 | 
			
		||||
	// The ID of the port which belongs to the load balancer
 | 
			
		||||
	PortID string `mapstructure:"port_id" json:"port_id"`
 | 
			
		||||
 | 
			
		||||
	// Indicates whether connections in the same session will be processed by the
 | 
			
		||||
	// same pool member or not.
 | 
			
		||||
	Persistence SessionPersistence `mapstructure:"session_persistence" json:"session_persistence"`
 | 
			
		||||
 | 
			
		||||
	// The maximum number of connections allowed for the VIP. Default is -1,
 | 
			
		||||
	// meaning no limit.
 | 
			
		||||
	ConnLimit int `mapstructure:"connection_limit" json:"connection_limit"`
 | 
			
		||||
 | 
			
		||||
	// The administrative state of the VIP. A valid value is true (UP) or false (DOWN).
 | 
			
		||||
	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
 | 
			
		||||
 | 
			
		||||
	// The status of the VIP. Indicates whether the VIP is operational.
 | 
			
		||||
	Status string `mapstructure:"status" json:"status"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VIPPage is the page returned by a pager when traversing over a
 | 
			
		||||
// collection of routers.
 | 
			
		||||
type VIPPage struct {
 | 
			
		||||
	pagination.LinkedPageBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextPageURL is invoked when a paginated collection of routers has reached
 | 
			
		||||
// the end of a page and the pager seeks to traverse over a new one. In order
 | 
			
		||||
// to do this, it needs to construct the next page's URL.
 | 
			
		||||
func (p VIPPage) NextPageURL() (string, error) {
 | 
			
		||||
	type resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"vips_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a RouterPage struct is empty.
 | 
			
		||||
func (p VIPPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractVIPs(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractVIPs accepts a Page struct, specifically a VIPPage struct,
 | 
			
		||||
// and extracts the elements into a slice of VirtualIP structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractVIPs(page pagination.Page) ([]VirtualIP, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		VIPs []VirtualIP `mapstructure:"vips" json:"vips"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(VIPPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.VIPs, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract is a function that accepts a result and extracts a router.
 | 
			
		||||
func (r commonResult) Extract() (*VirtualIP, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		VirtualIP *VirtualIP `mapstructure:"vip" json:"vip"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.VirtualIP, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateResult represents the result of a create operation.
 | 
			
		||||
type CreateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateResult represents the result of an update operation.
 | 
			
		||||
type UpdateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
package vips
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rootPath     = "lb"
 | 
			
		||||
	resourcePath = "vips"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath, id)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,214 +0,0 @@
 | 
			
		||||
// +build fixtures
 | 
			
		||||
 | 
			
		||||
package listeners
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	th "github.com/rackspace/gophercloud/testhelper"
 | 
			
		||||
	"github.com/rackspace/gophercloud/testhelper/client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ListenersListBody contains the canned body of a listeners list response.
 | 
			
		||||
const ListenersListBody = `
 | 
			
		||||
{
 | 
			
		||||
	"listeners":[
 | 
			
		||||
		{
 | 
			
		||||
			"id": "db902c0c-d5ff-4753-b465-668ad9656918",
 | 
			
		||||
			"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
 | 
			
		||||
			"name": "web",
 | 
			
		||||
			"description": "listener config for the web tier",
 | 
			
		||||
			"loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}],
 | 
			
		||||
			"protocol": "HTTP",
 | 
			
		||||
			"protocol_port": 80,
 | 
			
		||||
			"default_pool_id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
 | 
			
		||||
			"admin_state_up": true,
 | 
			
		||||
			"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
 | 
			
		||||
			"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
			"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
 | 
			
		||||
			"name": "db",
 | 
			
		||||
			"description": "listener config for the db tier",
 | 
			
		||||
			"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
 | 
			
		||||
			"protocol": "TCP",
 | 
			
		||||
			"protocol_port": 3306,
 | 
			
		||||
			"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
 | 
			
		||||
			"connection_limit": 2000,
 | 
			
		||||
			"admin_state_up": true,
 | 
			
		||||
			"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
 | 
			
		||||
			"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// SingleServerBody is the canned body of a Get request on an existing listener.
 | 
			
		||||
const SingleListenerBody = `
 | 
			
		||||
{
 | 
			
		||||
	"listener": {
 | 
			
		||||
		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
		"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
 | 
			
		||||
		"name": "db",
 | 
			
		||||
		"description": "listener config for the db tier",
 | 
			
		||||
		"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
 | 
			
		||||
		"protocol": "TCP",
 | 
			
		||||
		"protocol_port": 3306,
 | 
			
		||||
		"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
 | 
			
		||||
		"connection_limit": 2000,
 | 
			
		||||
		"admin_state_up": true,
 | 
			
		||||
		"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
 | 
			
		||||
		"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// PostUpdateListenerBody is the canned response body of a Update request on an existing listener.
 | 
			
		||||
const PostUpdateListenerBody = `
 | 
			
		||||
{
 | 
			
		||||
	"listener": {
 | 
			
		||||
		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
		"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
 | 
			
		||||
		"name": "NewListenerName",
 | 
			
		||||
		"description": "listener config for the db tier",
 | 
			
		||||
		"loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
 | 
			
		||||
		"protocol": "TCP",
 | 
			
		||||
		"protocol_port": 3306,
 | 
			
		||||
		"default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
 | 
			
		||||
		"connection_limit": 1000,
 | 
			
		||||
		"admin_state_up": true,
 | 
			
		||||
		"default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
 | 
			
		||||
		"sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ListenerWeb = Listener{
 | 
			
		||||
		ID:                     "db902c0c-d5ff-4753-b465-668ad9656918",
 | 
			
		||||
		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
 | 
			
		||||
		Name:                   "web",
 | 
			
		||||
		Description:            "listener config for the web tier",
 | 
			
		||||
		Loadbalancers:          []LoadBalancerID{{ID: "53306cda-815d-4354-9444-59e09da9c3c5"}},
 | 
			
		||||
		Protocol:               "HTTP",
 | 
			
		||||
		ProtocolPort:           80,
 | 
			
		||||
		DefaultPoolID:          "fad389a3-9a4a-4762-a365-8c7038508b5d",
 | 
			
		||||
		AdminStateUp:           true,
 | 
			
		||||
		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
 | 
			
		||||
		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
 | 
			
		||||
	}
 | 
			
		||||
	ListenerDb = Listener{
 | 
			
		||||
		ID:                     "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
 | 
			
		||||
		Name:                   "db",
 | 
			
		||||
		Description:            "listener config for the db tier",
 | 
			
		||||
		Loadbalancers:          []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
 | 
			
		||||
		Protocol:               "TCP",
 | 
			
		||||
		ProtocolPort:           3306,
 | 
			
		||||
		DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
 | 
			
		||||
		ConnLimit:              2000,
 | 
			
		||||
		AdminStateUp:           true,
 | 
			
		||||
		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
 | 
			
		||||
		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
 | 
			
		||||
	}
 | 
			
		||||
	ListenerUpdated = Listener{
 | 
			
		||||
		ID:                     "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
		TenantID:               "310df60f-2a10-4ee5-9554-98393092194c",
 | 
			
		||||
		Name:                   "NewListenerName",
 | 
			
		||||
		Description:            "listener config for the db tier",
 | 
			
		||||
		Loadbalancers:          []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
 | 
			
		||||
		Protocol:               "TCP",
 | 
			
		||||
		ProtocolPort:           3306,
 | 
			
		||||
		DefaultPoolID:          "41efe233-7591-43c5-9cf7-923964759f9e",
 | 
			
		||||
		ConnLimit:              1000,
 | 
			
		||||
		AdminStateUp:           true,
 | 
			
		||||
		DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76",
 | 
			
		||||
		SniContainerRefs:       []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"},
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HandleListenerListSuccessfully sets up the test server to respond to a listener List request.
 | 
			
		||||
func HandleListenerListSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
		r.ParseForm()
 | 
			
		||||
		marker := r.Form.Get("marker")
 | 
			
		||||
		switch marker {
 | 
			
		||||
		case "":
 | 
			
		||||
			fmt.Fprintf(w, ListenersListBody)
 | 
			
		||||
		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
 | 
			
		||||
			fmt.Fprintf(w, `{ "listeners": [] }`)
 | 
			
		||||
		default:
 | 
			
		||||
			t.Fatalf("/v2.0/lbaas/listeners invoked with unexpected marker=[%s]", marker)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleListenerCreationSuccessfully sets up the test server to respond to a listener creation request
 | 
			
		||||
// with a given response.
 | 
			
		||||
func HandleListenerCreationSuccessfully(t *testing.T, response string) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "POST")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestJSONRequest(t, r, `{
 | 
			
		||||
			    "listener": {
 | 
			
		||||
			        "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab",
 | 
			
		||||
			        "protocol": "TCP",
 | 
			
		||||
			        "name": "db",
 | 
			
		||||
			        "admin_state_up": true,
 | 
			
		||||
			        "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76",
 | 
			
		||||
			        "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e",
 | 
			
		||||
			        "protocol_port": 3306
 | 
			
		||||
			    }
 | 
			
		||||
		}`)
 | 
			
		||||
 | 
			
		||||
		w.WriteHeader(http.StatusAccepted)
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
		fmt.Fprintf(w, response)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleListenerGetSuccessfully sets up the test server to respond to a listener Get request.
 | 
			
		||||
func HandleListenerGetSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestHeader(t, r, "Accept", "application/json")
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, SingleListenerBody)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleListenerDeletionSuccessfully sets up the test server to respond to a listener deletion request.
 | 
			
		||||
func HandleListenerDeletionSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "DELETE")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.WriteHeader(http.StatusNoContent)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleListenerUpdateSuccessfully sets up the test server to respond to a listener Update request.
 | 
			
		||||
func HandleListenerUpdateSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "PUT")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestHeader(t, r, "Accept", "application/json")
 | 
			
		||||
		th.TestHeader(t, r, "Content-Type", "application/json")
 | 
			
		||||
		th.TestJSONRequest(t, r, `{
 | 
			
		||||
			"listener": {
 | 
			
		||||
				"name": "NewListenerName",
 | 
			
		||||
				"connection_limit": 1001
 | 
			
		||||
			}
 | 
			
		||||
		}`)
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, PostUpdateListenerBody)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@@ -1,279 +0,0 @@
 | 
			
		||||
package listeners
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AdminState gives users a solid type to work with for create and update
 | 
			
		||||
// operations. It is recommended that users use the `Up` and `Down` enums.
 | 
			
		||||
type AdminState *bool
 | 
			
		||||
 | 
			
		||||
type listenerOpts struct {
 | 
			
		||||
	// Required. The protocol - can either be TCP, HTTP or HTTPS.
 | 
			
		||||
	Protocol Protocol
 | 
			
		||||
 | 
			
		||||
	// Required. The port on which to listen for client traffic.
 | 
			
		||||
	ProtocolPort int
 | 
			
		||||
 | 
			
		||||
	// Required for admins. Indicates the owner of the Listener.
 | 
			
		||||
	TenantID string
 | 
			
		||||
 | 
			
		||||
	// Required. The load balancer on which to provision this listener.
 | 
			
		||||
	LoadbalancerID string
 | 
			
		||||
 | 
			
		||||
	// Human-readable name for the Listener. Does not have to be unique.
 | 
			
		||||
	Name string
 | 
			
		||||
 | 
			
		||||
	// Optional. The ID of the default pool with which the Listener is associated.
 | 
			
		||||
	DefaultPoolID string
 | 
			
		||||
 | 
			
		||||
	// Optional. Human-readable description for the Listener.
 | 
			
		||||
	Description string
 | 
			
		||||
 | 
			
		||||
	// Optional. The maximum number of connections allowed for the Listener.
 | 
			
		||||
	ConnLimit *int
 | 
			
		||||
 | 
			
		||||
	// Optional. A reference to a container of TLS secrets.
 | 
			
		||||
	DefaultTlsContainerRef string
 | 
			
		||||
 | 
			
		||||
	// Optional. A list of references to TLS secrets.
 | 
			
		||||
	SniContainerRefs []string
 | 
			
		||||
 | 
			
		||||
	// Optional. The administrative state of the Listener. A valid value is true (UP)
 | 
			
		||||
	// or false (DOWN).
 | 
			
		||||
	AdminStateUp *bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convenience vars for AdminStateUp values.
 | 
			
		||||
var (
 | 
			
		||||
	iTrue  = true
 | 
			
		||||
	iFalse = false
 | 
			
		||||
 | 
			
		||||
	Up   AdminState = &iTrue
 | 
			
		||||
	Down AdminState = &iFalse
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
// CreateResult will be returned.
 | 
			
		||||
//
 | 
			
		||||
// Users with an admin role can create Listeners on behalf of other tenants by
 | 
			
		||||
// specifying a TenantID attribute different than their own.
 | 
			
		||||
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 | 
			
		||||
	var res CreateResult
 | 
			
		||||
 | 
			
		||||
	reqBody, err := opts.ToListenerCreateMap()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		res.Err = err
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Send request to API
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular Listeners based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateOpts is the common options struct used in this package's Update
 | 
			
		||||
// operation.
 | 
			
		||||
type UpdateOpts listenerOpts
 | 
			
		||||
 | 
			
		||||
// ToListenerUpdateMap casts a UpdateOpts struct to a map.
 | 
			
		||||
func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) {
 | 
			
		||||
	l := make(map[string]interface{})
 | 
			
		||||
 | 
			
		||||
	if opts.Name != "" {
 | 
			
		||||
		l["name"] = opts.Name
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Description != "" {
 | 
			
		||||
		l["description"] = opts.Description
 | 
			
		||||
	}
 | 
			
		||||
	if opts.ConnLimit != nil {
 | 
			
		||||
		l["connection_limit"] = &opts.ConnLimit
 | 
			
		||||
	}
 | 
			
		||||
	if opts.DefaultTlsContainerRef != "" {
 | 
			
		||||
		l["default_tls_container_ref"] = opts.DefaultTlsContainerRef
 | 
			
		||||
	}
 | 
			
		||||
	if opts.SniContainerRefs != nil {
 | 
			
		||||
		l["sni_container_refs"] = opts.SniContainerRefs
 | 
			
		||||
	}
 | 
			
		||||
	if opts.AdminStateUp != nil {
 | 
			
		||||
		l["admin_state_up"] = &opts.AdminStateUp
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return map[string]interface{}{"listener": l}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update is an operation which modifies the attributes of the specified Listener.
 | 
			
		||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
 | 
			
		||||
	reqBody, err := opts.ToListenerUpdateMap()
 | 
			
		||||
	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},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete will permanently delete a particular Listeners based on its unique ID.
 | 
			
		||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
@@ -1,139 +0,0 @@
 | 
			
		||||
package listeners
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
type Listener struct {
 | 
			
		||||
	// The unique ID for the Listener.
 | 
			
		||||
	ID string `mapstructure:"id" json:"id"`
 | 
			
		||||
 | 
			
		||||
	// Owner of the Listener. Only an admin user can specify a tenant ID other than its own.
 | 
			
		||||
	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
 | 
			
		||||
 | 
			
		||||
	// Human-readable name for the Listener. Does not have to be unique.
 | 
			
		||||
	Name string `mapstructure:"name" json:"name"`
 | 
			
		||||
 | 
			
		||||
	// Human-readable description for the Listener.
 | 
			
		||||
	Description string `mapstructure:"description" json:"description"`
 | 
			
		||||
 | 
			
		||||
	// The protocol to loadbalance. A valid value is TCP, HTTP, or HTTPS.
 | 
			
		||||
	Protocol string `mapstructure:"protocol" json:"protocol"`
 | 
			
		||||
 | 
			
		||||
	// The port on which to listen to client traffic that is associated with the
 | 
			
		||||
	// Loadbalancer. A valid value is from 0 to 65535.
 | 
			
		||||
	ProtocolPort int `mapstructure:"protocol_port" json:"protocol_port"`
 | 
			
		||||
 | 
			
		||||
	// 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 IDs.
 | 
			
		||||
	Loadbalancers []LoadBalancerID `mapstructure:"loadbalancers" json:"loadbalancers"`
 | 
			
		||||
 | 
			
		||||
	// The maximum number of connections allowed for the Loadbalancer. Default is -1,
 | 
			
		||||
	// meaning no limit.
 | 
			
		||||
	ConnLimit int `mapstructure:"connection_limit" json:"connection_limit"`
 | 
			
		||||
 | 
			
		||||
	// The list of references to TLS secrets.
 | 
			
		||||
	SniContainerRefs []string `mapstructure:"sni_container_refs" json:"sni_container_refs"`
 | 
			
		||||
 | 
			
		||||
	// Optional. A reference to a container of TLS secrets.
 | 
			
		||||
	DefaultTlsContainerRef string `mapstructure:"default_tls_container_ref" json:"default_tls_container_ref"`
 | 
			
		||||
 | 
			
		||||
	// 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
 | 
			
		||||
// collection of routers.
 | 
			
		||||
type ListenerPage struct {
 | 
			
		||||
	pagination.LinkedPageBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextPageURL is invoked when a paginated collection of routers has reached
 | 
			
		||||
// the end of a page and the pager seeks to traverse over a new one. In order
 | 
			
		||||
// to do this, it needs to construct the next page's URL.
 | 
			
		||||
func (p ListenerPage) NextPageURL() (string, error) {
 | 
			
		||||
	type resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"listeners_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a RouterPage struct is empty.
 | 
			
		||||
func (p ListenerPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractListeners(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractListeners accepts a Page struct, specifically a ListenerPage struct,
 | 
			
		||||
// and extracts the elements into a slice of Listener structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractListeners(page pagination.Page) ([]Listener, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		Listeners []Listener `mapstructure:"listeners" json:"listeners"`
 | 
			
		||||
	}
 | 
			
		||||
	err := mapstructure.Decode(page.(ListenerPage).Body, &resp)
 | 
			
		||||
	return resp.Listeners, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract is a function that accepts a result and extracts a router.
 | 
			
		||||
func (r commonResult) Extract() (*Listener, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		Listener *Listener `mapstructure:"listener" json:"listener"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.Listener, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateResult represents the result of a create operation.
 | 
			
		||||
type CreateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateResult represents the result of an update operation.
 | 
			
		||||
type UpdateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
package listeners
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rootPath     = "lbaas"
 | 
			
		||||
	resourcePath = "listeners"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath, id)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,278 +0,0 @@
 | 
			
		||||
// +build fixtures
 | 
			
		||||
 | 
			
		||||
package loadbalancers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	th "github.com/rackspace/gophercloud/testhelper"
 | 
			
		||||
	"github.com/rackspace/gophercloud/testhelper/client"
 | 
			
		||||
 | 
			
		||||
	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
 | 
			
		||||
	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
 | 
			
		||||
	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LoadbalancersListBody contains the canned body of a loadbalancer list response.
 | 
			
		||||
const LoadbalancersListBody = `
 | 
			
		||||
{
 | 
			
		||||
	"loadbalancers":[
 | 
			
		||||
	         {
 | 
			
		||||
			"id": "c331058c-6a40-4144-948e-b9fb1df9db4b",
 | 
			
		||||
			"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
 | 
			
		||||
			"name": "web_lb",
 | 
			
		||||
			"description": "lb config for the web tier",
 | 
			
		||||
			"vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154",
 | 
			
		||||
			"vip_address": "10.30.176.47",
 | 
			
		||||
			"flavor": "small",
 | 
			
		||||
			"provider": "haproxy",
 | 
			
		||||
			"admin_state_up": true,
 | 
			
		||||
			"provisioning_status": "ACTIVE",
 | 
			
		||||
			"operating_status": "ONLINE"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
			"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
 | 
			
		||||
			"name": "db_lb",
 | 
			
		||||
			"description": "lb config for the db tier",
 | 
			
		||||
			"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
 | 
			
		||||
			"vip_address": "10.30.176.48",
 | 
			
		||||
			"flavor": "medium",
 | 
			
		||||
			"provider": "haproxy",
 | 
			
		||||
			"admin_state_up": true,
 | 
			
		||||
			"provisioning_status": "PENDING_CREATE",
 | 
			
		||||
			"operating_status": "OFFLINE"
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer.
 | 
			
		||||
const SingleLoadbalancerBody = `
 | 
			
		||||
{
 | 
			
		||||
	"loadbalancer": {
 | 
			
		||||
		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
		"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
 | 
			
		||||
		"name": "db_lb",
 | 
			
		||||
		"description": "lb config for the db tier",
 | 
			
		||||
		"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
 | 
			
		||||
		"vip_address": "10.30.176.48",
 | 
			
		||||
		"flavor": "medium",
 | 
			
		||||
		"provider": "haproxy",
 | 
			
		||||
		"admin_state_up": true,
 | 
			
		||||
		"provisioning_status": "PENDING_CREATE",
 | 
			
		||||
		"operating_status": "OFFLINE"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// PostUpdateLoadbalancerBody is the canned response body of a Update request on an existing loadbalancer.
 | 
			
		||||
const PostUpdateLoadbalancerBody = `
 | 
			
		||||
{
 | 
			
		||||
	"loadbalancer": {
 | 
			
		||||
		"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
		"tenant_id": "54030507-44f7-473c-9342-b4d14a95f692",
 | 
			
		||||
		"name": "NewLoadbalancerName",
 | 
			
		||||
		"description": "lb config for the db tier",
 | 
			
		||||
		"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
 | 
			
		||||
		"vip_address": "10.30.176.48",
 | 
			
		||||
		"flavor": "medium",
 | 
			
		||||
		"provider": "haproxy",
 | 
			
		||||
		"admin_state_up": true,
 | 
			
		||||
		"provisioning_status": "PENDING_CREATE",
 | 
			
		||||
		"operating_status": "OFFLINE"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer.
 | 
			
		||||
const LoadbalancerStatuesesTree = `
 | 
			
		||||
{
 | 
			
		||||
	"statuses" : {
 | 
			
		||||
		"loadbalancer": {
 | 
			
		||||
			"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
			"name": "db_lb",
 | 
			
		||||
			"provisioning_status": "PENDING_UPDATE",
 | 
			
		||||
			"operating_status": "ACTIVE",
 | 
			
		||||
			"listeners": [{
 | 
			
		||||
				"id": "db902c0c-d5ff-4753-b465-668ad9656918",
 | 
			
		||||
				"name": "db",
 | 
			
		||||
				"pools": [{
 | 
			
		||||
					"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
 | 
			
		||||
					"name": "db",
 | 
			
		||||
					"healthmonitor": {
 | 
			
		||||
						"id": "67306cda-815d-4354-9fe4-59e09da9c3c5",
 | 
			
		||||
						"type":"PING"
 | 
			
		||||
					},
 | 
			
		||||
					"members":[{
 | 
			
		||||
						"id": "2a280670-c202-4b0b-a562-34077415aabf",
 | 
			
		||||
						"name": "db",
 | 
			
		||||
						"address": "10.0.2.11",
 | 
			
		||||
						"protocol_port": 80
 | 
			
		||||
					}]
 | 
			
		||||
				}]
 | 
			
		||||
			}]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	LoadbalancerWeb = LoadBalancer{
 | 
			
		||||
		ID:                 "c331058c-6a40-4144-948e-b9fb1df9db4b",
 | 
			
		||||
		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
 | 
			
		||||
		Name:               "web_lb",
 | 
			
		||||
		Description:        "lb config for the web tier",
 | 
			
		||||
		VipSubnetID:        "8a49c438-848f-467b-9655-ea1548708154",
 | 
			
		||||
		VipAddress:         "10.30.176.47",
 | 
			
		||||
		Flavor:             "small",
 | 
			
		||||
		Provider:           "haproxy",
 | 
			
		||||
		AdminStateUp:       true,
 | 
			
		||||
		ProvisioningStatus: "ACTIVE",
 | 
			
		||||
		OperatingStatus:    "ONLINE",
 | 
			
		||||
	}
 | 
			
		||||
	LoadbalancerDb = LoadBalancer{
 | 
			
		||||
		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
 | 
			
		||||
		Name:               "db_lb",
 | 
			
		||||
		Description:        "lb config for the db tier",
 | 
			
		||||
		VipSubnetID:        "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
 | 
			
		||||
		VipAddress:         "10.30.176.48",
 | 
			
		||||
		Flavor:             "medium",
 | 
			
		||||
		Provider:           "haproxy",
 | 
			
		||||
		AdminStateUp:       true,
 | 
			
		||||
		ProvisioningStatus: "PENDING_CREATE",
 | 
			
		||||
		OperatingStatus:    "OFFLINE",
 | 
			
		||||
	}
 | 
			
		||||
	LoadbalancerUpdated = LoadBalancer{
 | 
			
		||||
		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
		TenantID:           "54030507-44f7-473c-9342-b4d14a95f692",
 | 
			
		||||
		Name:               "NewLoadbalancerName",
 | 
			
		||||
		Description:        "lb config for the db tier",
 | 
			
		||||
		VipSubnetID:        "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
 | 
			
		||||
		VipAddress:         "10.30.176.48",
 | 
			
		||||
		Flavor:             "medium",
 | 
			
		||||
		Provider:           "haproxy",
 | 
			
		||||
		AdminStateUp:       true,
 | 
			
		||||
		ProvisioningStatus: "PENDING_CREATE",
 | 
			
		||||
		OperatingStatus:    "OFFLINE",
 | 
			
		||||
	}
 | 
			
		||||
	LoadbalancerStatusesTree = LoadBalancer{
 | 
			
		||||
		ID:                 "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
 | 
			
		||||
		Name:               "db_lb",
 | 
			
		||||
		ProvisioningStatus: "PENDING_UPDATE",
 | 
			
		||||
		OperatingStatus:    "ACTIVE",
 | 
			
		||||
		Listeners: []listeners.Listener{{
 | 
			
		||||
			ID:   "db902c0c-d5ff-4753-b465-668ad9656918",
 | 
			
		||||
			Name: "db",
 | 
			
		||||
			Pools: []pools.Pool{{
 | 
			
		||||
				ID:   "fad389a3-9a4a-4762-a365-8c7038508b5d",
 | 
			
		||||
				Name: "db",
 | 
			
		||||
				Monitor: monitors.Monitor{
 | 
			
		||||
					ID:   "67306cda-815d-4354-9fe4-59e09da9c3c5",
 | 
			
		||||
					Type: "PING",
 | 
			
		||||
				},
 | 
			
		||||
				Members: []pools.Member{{
 | 
			
		||||
					ID:           "2a280670-c202-4b0b-a562-34077415aabf",
 | 
			
		||||
					Name:         "db",
 | 
			
		||||
					Address:      "10.0.2.11",
 | 
			
		||||
					ProtocolPort: 80,
 | 
			
		||||
				}},
 | 
			
		||||
			}},
 | 
			
		||||
		}},
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HandleLoadbalancerListSuccessfully sets up the test server to respond to a loadbalancer List request.
 | 
			
		||||
func HandleLoadbalancerListSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
		r.ParseForm()
 | 
			
		||||
		marker := r.Form.Get("marker")
 | 
			
		||||
		switch marker {
 | 
			
		||||
		case "":
 | 
			
		||||
			fmt.Fprintf(w, LoadbalancersListBody)
 | 
			
		||||
		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
 | 
			
		||||
			fmt.Fprintf(w, `{ "loadbalancers": [] }`)
 | 
			
		||||
		default:
 | 
			
		||||
			t.Fatalf("/v2.0/lbaas/loadbalancers invoked with unexpected marker=[%s]", marker)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleLoadbalancerCreationSuccessfully sets up the test server to respond to a loadbalancer creation request
 | 
			
		||||
// with a given response.
 | 
			
		||||
func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "POST")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestJSONRequest(t, r, `{
 | 
			
		||||
			"loadbalancer": {
 | 
			
		||||
				"name": "db_lb",
 | 
			
		||||
				"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
 | 
			
		||||
				"vip_address": "10.30.176.48",
 | 
			
		||||
				"flavor": "medium",
 | 
			
		||||
				"provider": "haproxy",
 | 
			
		||||
				"admin_state_up": true
 | 
			
		||||
			}
 | 
			
		||||
		}`)
 | 
			
		||||
 | 
			
		||||
		w.WriteHeader(http.StatusAccepted)
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
		fmt.Fprintf(w, response)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleLoadbalancerGetSuccessfully sets up the test server to respond to a loadbalancer Get request.
 | 
			
		||||
func HandleLoadbalancerGetSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestHeader(t, r, "Accept", "application/json")
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, SingleLoadbalancerBody)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleLoadbalancerGetStatusesTree sets up the test server to respond to a loadbalancer Get statuses tree request.
 | 
			
		||||
func HandleLoadbalancerGetStatusesTree(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/statuses", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestHeader(t, r, "Accept", "application/json")
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, LoadbalancerStatuesesTree)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleLoadbalancerDeletionSuccessfully sets up the test server to respond to a loadbalancer deletion request.
 | 
			
		||||
func HandleLoadbalancerDeletionSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "DELETE")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.WriteHeader(http.StatusNoContent)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleLoadbalancerUpdateSuccessfully sets up the test server to respond to a loadbalancer Update request.
 | 
			
		||||
func HandleLoadbalancerUpdateSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "PUT")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestHeader(t, r, "Accept", "application/json")
 | 
			
		||||
		th.TestHeader(t, r, "Content-Type", "application/json")
 | 
			
		||||
		th.TestJSONRequest(t, r, `{
 | 
			
		||||
			"loadbalancer": {
 | 
			
		||||
				"name": "NewLoadbalancerName"
 | 
			
		||||
			}
 | 
			
		||||
		}`)
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, PostUpdateLoadbalancerBody)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@@ -1,248 +0,0 @@
 | 
			
		||||
package loadbalancers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AdminState gives users a solid type to work with for create and update
 | 
			
		||||
// operations. It is recommended that users use the `Up` and `Down` enums.
 | 
			
		||||
type AdminState *bool
 | 
			
		||||
 | 
			
		||||
type loadbalancerOpts struct {
 | 
			
		||||
	// Optional. Human-readable name for the Loadbalancer. Does not have to be unique.
 | 
			
		||||
	Name string
 | 
			
		||||
 | 
			
		||||
	// Optional. Human-readable description for the Loadbalancer.
 | 
			
		||||
	Description string
 | 
			
		||||
 | 
			
		||||
	// Required. The network on which to allocate the Loadbalancer's address. A tenant can
 | 
			
		||||
	// only create Loadbalancers on networks authorized by policy (e.g. networks that
 | 
			
		||||
	// belong to them or networks that are shared).
 | 
			
		||||
	VipSubnetID string
 | 
			
		||||
 | 
			
		||||
	// Required for admins. The UUID of the tenant who owns the Loadbalancer.
 | 
			
		||||
	// Only administrative users can specify a tenant UUID other than their own.
 | 
			
		||||
	TenantID string
 | 
			
		||||
 | 
			
		||||
	// Optional. The IP address of the Loadbalancer.
 | 
			
		||||
	VipAddress string
 | 
			
		||||
 | 
			
		||||
	// Optional. The administrative state of the Loadbalancer. A valid value is true (UP)
 | 
			
		||||
	// or false (DOWN).
 | 
			
		||||
	AdminStateUp *bool
 | 
			
		||||
 | 
			
		||||
	// Optional. The UUID of a flavor.
 | 
			
		||||
	Flavor string
 | 
			
		||||
 | 
			
		||||
	// Optional. The name of the provider.
 | 
			
		||||
	Provider string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convenience vars for AdminStateUp values.
 | 
			
		||||
var (
 | 
			
		||||
	iTrue  = true
 | 
			
		||||
	iFalse = false
 | 
			
		||||
 | 
			
		||||
	Up   AdminState = &iTrue
 | 
			
		||||
	Down AdminState = &iFalse
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
// CreateResult will be returned.
 | 
			
		||||
//
 | 
			
		||||
// Users with an admin role can create loadbalancers on behalf of other tenants by
 | 
			
		||||
// specifying a TenantID attribute different than their own.
 | 
			
		||||
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 | 
			
		||||
	var res CreateResult
 | 
			
		||||
 | 
			
		||||
	reqBody, err := opts.ToLoadbalancerCreateMap()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		res.Err = err
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Send request to API
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateOpts is the common options struct used in this package's Update
 | 
			
		||||
// operation.
 | 
			
		||||
type UpdateOpts loadbalancerOpts
 | 
			
		||||
 | 
			
		||||
// ToLoadbalancerUpdateMap casts a UpdateOpts struct to a map.
 | 
			
		||||
func (opts UpdateOpts) ToLoadbalancerUpdateMap() (map[string]interface{}, error) {
 | 
			
		||||
	l := make(map[string]interface{})
 | 
			
		||||
 | 
			
		||||
	if opts.Name != "" {
 | 
			
		||||
		l["name"] = opts.Name
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Description != "" {
 | 
			
		||||
		l["description"] = opts.Description
 | 
			
		||||
	}
 | 
			
		||||
	if opts.AdminStateUp != nil {
 | 
			
		||||
		l["admin_state_up"] = &opts.AdminStateUp
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return map[string]interface{}{"loadbalancer": l}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update is an operation which modifies the attributes of the specified Loadbalancer.
 | 
			
		||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
 | 
			
		||||
	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},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete will permanently delete a particular Loadbalancer based on its unique ID.
 | 
			
		||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetStatuses(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(statusRootURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
@@ -1,146 +0,0 @@
 | 
			
		||||
package loadbalancers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
type LoadBalancer struct {
 | 
			
		||||
	// Human-readable description for the Loadbalancer.
 | 
			
		||||
	Description string `mapstructure:"description" json:"description"`
 | 
			
		||||
 | 
			
		||||
	// The administrative state of the Loadbalancer. A valid value is true (UP) or false (DOWN).
 | 
			
		||||
	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
 | 
			
		||||
 | 
			
		||||
	// Owner of the LoadBalancer. Only an admin user can specify a tenant ID other than its own.
 | 
			
		||||
	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
 | 
			
		||||
 | 
			
		||||
	// The provisioning status of the LoadBalancer. This value is ACTIVE, PENDING_CREATE or ERROR.
 | 
			
		||||
	ProvisioningStatus string `mapstructure:"provisioning_status" json:"provisioning_status"`
 | 
			
		||||
 | 
			
		||||
	// The IP address of the Loadbalancer.
 | 
			
		||||
	VipAddress string `mapstructure:"vip_address" json:"vip_address"`
 | 
			
		||||
 | 
			
		||||
	// The UUID of the subnet on which to allocate the virtual IP for the Loadbalancer address.
 | 
			
		||||
	VipSubnetID string `mapstructure:"vip_subnet_id" json:"vip_subnet_id"`
 | 
			
		||||
 | 
			
		||||
	// The unique ID for the LoadBalancer.
 | 
			
		||||
	ID string `mapstructure:"id" json:"id"`
 | 
			
		||||
 | 
			
		||||
	// The operating status of the LoadBalancer. This value is ONLINE or OFFLINE.
 | 
			
		||||
	OperatingStatus string `mapstructure:"operating_status" json:"operating_status"`
 | 
			
		||||
 | 
			
		||||
	// Human-readable name for the LoadBalancer. Does not have to be unique.
 | 
			
		||||
	Name string `mapstructure:"name" json:"name"`
 | 
			
		||||
 | 
			
		||||
	// The UUID of a flavor if set.
 | 
			
		||||
	Flavor string `mapstructure:"flavor" json:"flavor"`
 | 
			
		||||
 | 
			
		||||
	// 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
 | 
			
		||||
// collection of routers.
 | 
			
		||||
type LoadbalancerPage struct {
 | 
			
		||||
	pagination.LinkedPageBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextPageURL is invoked when a paginated collection of routers has reached
 | 
			
		||||
// the end of a page and the pager seeks to traverse over a new one. In order
 | 
			
		||||
// to do this, it needs to construct the next page's URL.
 | 
			
		||||
func (p LoadbalancerPage) NextPageURL() (string, error) {
 | 
			
		||||
	type resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"loadbalancers_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a RouterPage struct is empty.
 | 
			
		||||
func (p LoadbalancerPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractLoadbalancers(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractLoadbalancers accepts a Page struct, specifically a LoadbalancerPage struct,
 | 
			
		||||
// and extracts the elements into a slice of LoadBalancer structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractLoadbalancers(page pagination.Page) ([]LoadBalancer, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		LoadBalancers []LoadBalancer `mapstructure:"loadbalancers" json:"loadbalancers"`
 | 
			
		||||
	}
 | 
			
		||||
	err := mapstructure.Decode(page.(LoadbalancerPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.LoadBalancers, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract is a function that accepts a result and extracts a router.
 | 
			
		||||
func (r commonResult) Extract() (*LoadBalancer, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
	var res struct {
 | 
			
		||||
		LoadBalancer *LoadBalancer `mapstructure:"loadbalancer" json:"loadbalancer"`
 | 
			
		||||
	}
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateResult represents the result of an update operation.
 | 
			
		||||
type UpdateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
package loadbalancers
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rootPath     = "lbaas"
 | 
			
		||||
	resourcePath = "loadbalancers"
 | 
			
		||||
	statusPath   = "statuses"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,216 +0,0 @@
 | 
			
		||||
// +build fixtures
 | 
			
		||||
 | 
			
		||||
package monitors
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	th "github.com/rackspace/gophercloud/testhelper"
 | 
			
		||||
	"github.com/rackspace/gophercloud/testhelper/client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HealthmonitorsListBody contains the canned body of a healthmonitor list response.
 | 
			
		||||
const HealthmonitorsListBody = `
 | 
			
		||||
{
 | 
			
		||||
	"healthmonitors":[
 | 
			
		||||
		{
 | 
			
		||||
			"admin_state_up":true,
 | 
			
		||||
			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
			"delay":10,
 | 
			
		||||
			"name":"web",
 | 
			
		||||
			"max_retries":1,
 | 
			
		||||
			"timeout":1,
 | 
			
		||||
			"type":"PING",
 | 
			
		||||
			"pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}],
 | 
			
		||||
			"id":"466c8345-28d8-4f84-a246-e04380b0461d"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"admin_state_up":true,
 | 
			
		||||
			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
			"delay":5,
 | 
			
		||||
			"name":"db",
 | 
			
		||||
			"expected_codes":"200",
 | 
			
		||||
			"max_retries":2,
 | 
			
		||||
			"http_method":"GET",
 | 
			
		||||
			"timeout":2,
 | 
			
		||||
			"url_path":"/",
 | 
			
		||||
			"type":"HTTP",
 | 
			
		||||
			"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
 | 
			
		||||
			"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// SingleHealthmonitorBody is the canned body of a Get request on an existing healthmonitor.
 | 
			
		||||
const SingleHealthmonitorBody = `
 | 
			
		||||
{
 | 
			
		||||
	"healthmonitor": {
 | 
			
		||||
		"admin_state_up":true,
 | 
			
		||||
		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
		"delay":5,
 | 
			
		||||
		"name":"db",
 | 
			
		||||
		"expected_codes":"200",
 | 
			
		||||
		"max_retries":2,
 | 
			
		||||
		"http_method":"GET",
 | 
			
		||||
		"timeout":2,
 | 
			
		||||
		"url_path":"/",
 | 
			
		||||
		"type":"HTTP",
 | 
			
		||||
		"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
 | 
			
		||||
		"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// PostUpdateHealthmonitorBody is the canned response body of a Update request on an existing healthmonitor.
 | 
			
		||||
const PostUpdateHealthmonitorBody = `
 | 
			
		||||
{
 | 
			
		||||
	"healthmonitor": {
 | 
			
		||||
		"admin_state_up":true,
 | 
			
		||||
		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
		"delay":3,
 | 
			
		||||
		"name":"NewHealthmonitorName",
 | 
			
		||||
		"expected_codes":"301",
 | 
			
		||||
		"max_retries":10,
 | 
			
		||||
		"http_method":"GET",
 | 
			
		||||
		"timeout":20,
 | 
			
		||||
		"url_path":"/another_check",
 | 
			
		||||
		"type":"HTTP",
 | 
			
		||||
		"pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}],
 | 
			
		||||
		"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	HealthmonitorWeb = Monitor{
 | 
			
		||||
		AdminStateUp: true,
 | 
			
		||||
		Name:         "web",
 | 
			
		||||
		TenantID:     "83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
		Delay:        10,
 | 
			
		||||
		MaxRetries:   1,
 | 
			
		||||
		Timeout:      1,
 | 
			
		||||
		Type:         "PING",
 | 
			
		||||
		ID:           "466c8345-28d8-4f84-a246-e04380b0461d",
 | 
			
		||||
		Pools:        []PoolID{{ID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}},
 | 
			
		||||
	}
 | 
			
		||||
	HealthmonitorDb = Monitor{
 | 
			
		||||
		AdminStateUp:  true,
 | 
			
		||||
		Name:          "db",
 | 
			
		||||
		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
		Delay:         5,
 | 
			
		||||
		ExpectedCodes: "200",
 | 
			
		||||
		MaxRetries:    2,
 | 
			
		||||
		Timeout:       2,
 | 
			
		||||
		URLPath:       "/",
 | 
			
		||||
		Type:          "HTTP",
 | 
			
		||||
		HTTPMethod:    "GET",
 | 
			
		||||
		ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
 | 
			
		||||
		Pools:         []PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}},
 | 
			
		||||
	}
 | 
			
		||||
	HealthmonitorUpdated = Monitor{
 | 
			
		||||
		AdminStateUp:  true,
 | 
			
		||||
		Name:          "NewHealthmonitorName",
 | 
			
		||||
		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
		Delay:         3,
 | 
			
		||||
		ExpectedCodes: "301",
 | 
			
		||||
		MaxRetries:    10,
 | 
			
		||||
		Timeout:       20,
 | 
			
		||||
		URLPath:       "/another_check",
 | 
			
		||||
		Type:          "HTTP",
 | 
			
		||||
		HTTPMethod:    "GET",
 | 
			
		||||
		ID:            "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
 | 
			
		||||
		Pools:         []PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}},
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HandleHealthmonitorListSuccessfully sets up the test server to respond to a healthmonitor List request.
 | 
			
		||||
func HandleHealthmonitorListSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
		r.ParseForm()
 | 
			
		||||
		marker := r.Form.Get("marker")
 | 
			
		||||
		switch marker {
 | 
			
		||||
		case "":
 | 
			
		||||
			fmt.Fprintf(w, HealthmonitorsListBody)
 | 
			
		||||
		case "556c8345-28d8-4f84-a246-e04380b0461d":
 | 
			
		||||
			fmt.Fprintf(w, `{ "healthmonitors": [] }`)
 | 
			
		||||
		default:
 | 
			
		||||
			t.Fatalf("/v2.0/lbaas/healthmonitors invoked with unexpected marker=[%s]", marker)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleHealthmonitorCreationSuccessfully sets up the test server to respond to a healthmonitor creation request
 | 
			
		||||
// with a given response.
 | 
			
		||||
func HandleHealthmonitorCreationSuccessfully(t *testing.T, response string) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "POST")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestJSONRequest(t, r, `{
 | 
			
		||||
			"healthmonitor": {
 | 
			
		||||
				"type":"HTTP",
 | 
			
		||||
				"pool_id":"84f1b61f-58c4-45bf-a8a9-2dafb9e5214d",
 | 
			
		||||
				"tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
 | 
			
		||||
				"delay":20,
 | 
			
		||||
				"name":"db",
 | 
			
		||||
				"timeout":10,
 | 
			
		||||
				"max_retries":5,
 | 
			
		||||
				"url_path":"/check",
 | 
			
		||||
				"expected_codes":"200-299"
 | 
			
		||||
			}
 | 
			
		||||
		}`)
 | 
			
		||||
 | 
			
		||||
		w.WriteHeader(http.StatusAccepted)
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
		fmt.Fprintf(w, response)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleHealthmonitorGetSuccessfully sets up the test server to respond to a healthmonitor Get request.
 | 
			
		||||
func HandleHealthmonitorGetSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestHeader(t, r, "Accept", "application/json")
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, SingleHealthmonitorBody)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleHealthmonitorDeletionSuccessfully sets up the test server to respond to a healthmonitor deletion request.
 | 
			
		||||
func HandleHealthmonitorDeletionSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "DELETE")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.WriteHeader(http.StatusNoContent)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleHealthmonitorUpdateSuccessfully sets up the test server to respond to a healthmonitor Update request.
 | 
			
		||||
func HandleHealthmonitorUpdateSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "PUT")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestHeader(t, r, "Accept", "application/json")
 | 
			
		||||
		th.TestHeader(t, r, "Content-Type", "application/json")
 | 
			
		||||
		th.TestJSONRequest(t, r, `{
 | 
			
		||||
			"healthmonitor": {
 | 
			
		||||
				"name": "NewHealthmonitorName",
 | 
			
		||||
				"delay": 3,
 | 
			
		||||
				"timeout": 20,
 | 
			
		||||
				"max_retries": 10,
 | 
			
		||||
				"url_path": "/another_check",
 | 
			
		||||
				"expected_codes": "301"
 | 
			
		||||
			}
 | 
			
		||||
		}`)
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, PostUpdateHealthmonitorBody)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@@ -1,304 +0,0 @@
 | 
			
		||||
package monitors
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type monitorOpts struct {
 | 
			
		||||
	// Required. The Pool to Monitor.
 | 
			
		||||
	PoolID string
 | 
			
		||||
 | 
			
		||||
	// Optional. The Name of the Monitor.
 | 
			
		||||
	Name string
 | 
			
		||||
 | 
			
		||||
	// Required for admins. Indicates the owner of the Loadbalancer.
 | 
			
		||||
	TenantID string
 | 
			
		||||
 | 
			
		||||
	// Required. The type of probe, which is PING, TCP, HTTP, or HTTPS, that is
 | 
			
		||||
	// sent by the load balancer to verify the member state.
 | 
			
		||||
	Type string
 | 
			
		||||
 | 
			
		||||
	// Required. The time, in seconds, between sending probes to members.
 | 
			
		||||
	Delay int
 | 
			
		||||
 | 
			
		||||
	// Required. Maximum number of seconds for a Monitor to wait for a ping reply
 | 
			
		||||
	// before it times out. The value must be less than the delay value.
 | 
			
		||||
	Timeout int
 | 
			
		||||
 | 
			
		||||
	// Required. Number of permissible ping failures before changing the member's
 | 
			
		||||
	// status to INACTIVE. Must be a number between 1 and 10.
 | 
			
		||||
	MaxRetries int
 | 
			
		||||
 | 
			
		||||
	// Required for HTTP(S) types. URI path that will be accessed if Monitor type
 | 
			
		||||
	// is HTTP or HTTPS.
 | 
			
		||||
	URLPath string
 | 
			
		||||
 | 
			
		||||
	// Required for HTTP(S) types. The HTTP method used for requests by the
 | 
			
		||||
	// Monitor. If this attribute is not specified, it defaults to "GET".
 | 
			
		||||
	HTTPMethod string
 | 
			
		||||
 | 
			
		||||
	// Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S)
 | 
			
		||||
	// Monitor. You can either specify a single status like "200", or a range
 | 
			
		||||
	// like "200-202".
 | 
			
		||||
	ExpectedCodes string
 | 
			
		||||
 | 
			
		||||
	AdminStateUp *bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListOptsBuilder allows extensions to add additional parameters to the
 | 
			
		||||
// List request.
 | 
			
		||||
type ListOptsBuilder interface {
 | 
			
		||||
	ToMonitorListQuery() (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 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 allowed[opts.Type] {
 | 
			
		||||
		l["type"] = opts.Type
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, errValidTypeRequired
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Type == TypeHTTP || opts.Type == TypeHTTPS {
 | 
			
		||||
		if opts.URLPath != "" {
 | 
			
		||||
			l["url_path"] = opts.URLPath
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil, errURLPathRequired
 | 
			
		||||
		}
 | 
			
		||||
		if opts.ExpectedCodes != "" {
 | 
			
		||||
			l["expected_codes"] = opts.ExpectedCodes
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil, errExpectedCodesRequired
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if opts.PoolID != "" {
 | 
			
		||||
		l["pool_id"] = opts.PoolID
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, errPoolIDRequired
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Delay != 0 {
 | 
			
		||||
		l["delay"] = opts.Delay
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, errDelayRequired
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Timeout != 0 {
 | 
			
		||||
		l["timeout"] = opts.Timeout
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, errMaxRetriesRequired
 | 
			
		||||
	}
 | 
			
		||||
	if opts.MaxRetries != 0 {
 | 
			
		||||
		l["max_retries"] = opts.MaxRetries
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, errMaxRetriesRequired
 | 
			
		||||
	}
 | 
			
		||||
	if opts.AdminStateUp != nil {
 | 
			
		||||
		l["admin_state_up"] = &opts.AdminStateUp
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Name != "" {
 | 
			
		||||
		l["name"] = opts.Name
 | 
			
		||||
	}
 | 
			
		||||
	if opts.TenantID != "" {
 | 
			
		||||
		l["tenant_id"] = opts.TenantID
 | 
			
		||||
	}
 | 
			
		||||
	if opts.HTTPMethod != "" {
 | 
			
		||||
		l["http_method"] = opts.HTTPMethod
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Send request to API
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular Health Monitor based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateOpts is the common options struct used in this package's Update
 | 
			
		||||
// operation.
 | 
			
		||||
type UpdateOpts monitorOpts
 | 
			
		||||
 | 
			
		||||
// ToMonitorUpdateMap casts a UpdateOpts struct to a map.
 | 
			
		||||
func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]interface{}, error) {
 | 
			
		||||
	l := make(map[string]interface{})
 | 
			
		||||
 | 
			
		||||
	if opts.URLPath != "" {
 | 
			
		||||
		l["url_path"] = opts.URLPath
 | 
			
		||||
	}
 | 
			
		||||
	if opts.ExpectedCodes != "" {
 | 
			
		||||
		l["expected_codes"] = opts.ExpectedCodes
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Delay != 0 {
 | 
			
		||||
		l["delay"] = opts.Delay
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Timeout != 0 {
 | 
			
		||||
		l["timeout"] = opts.Timeout
 | 
			
		||||
	}
 | 
			
		||||
	if opts.MaxRetries != 0 {
 | 
			
		||||
		l["max_retries"] = opts.MaxRetries
 | 
			
		||||
	}
 | 
			
		||||
	if opts.AdminStateUp != nil {
 | 
			
		||||
		l["admin_state_up"] = &opts.AdminStateUp
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Name != "" {
 | 
			
		||||
		l["name"] = opts.Name
 | 
			
		||||
	}
 | 
			
		||||
	if opts.HTTPMethod != "" {
 | 
			
		||||
		l["http_method"] = opts.HTTPMethod
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return map[string]interface{}{"healthmonitor": l}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update is an operation which modifies the attributes of the specified Monitor.
 | 
			
		||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
 | 
			
		||||
	reqBody, err := opts.ToMonitorUpdateMap()
 | 
			
		||||
	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},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
@@ -1,160 +0,0 @@
 | 
			
		||||
package monitors
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"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
 | 
			
		||||
// 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 Monitor.
 | 
			
		||||
	ID string
 | 
			
		||||
 | 
			
		||||
	// 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"`
 | 
			
		||||
 | 
			
		||||
	// 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
 | 
			
		||||
 | 
			
		||||
	// List of pools that are associated with the health monitor.
 | 
			
		||||
	Pools []PoolID `mapstructure:"pools" json:"pools"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Pool struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"healthmonitors_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a PoolPage struct is empty.
 | 
			
		||||
func (p MonitorPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractMonitors(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractMonitors accepts a Page struct, specifically a MonitorPage struct,
 | 
			
		||||
// and extracts the elements into a slice of Monitor structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractMonitors(page pagination.Page) ([]Monitor, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		Monitors []Monitor `mapstructure:"healthmonitors" json:"healthmonitors"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(MonitorPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.Monitors, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.Monitor, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateResult represents the result of a create operation.
 | 
			
		||||
type CreateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateResult represents the result of an update operation.
 | 
			
		||||
type UpdateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
package monitors
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rootPath     = "lbaas"
 | 
			
		||||
	resourcePath = "healthmonitors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath, id)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,389 +0,0 @@
 | 
			
		||||
// +build fixtures
 | 
			
		||||
 | 
			
		||||
package pools
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	th "github.com/rackspace/gophercloud/testhelper"
 | 
			
		||||
	"github.com/rackspace/gophercloud/testhelper/client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PoolsListBody contains the canned body of a pool list response.
 | 
			
		||||
const PoolsListBody = `
 | 
			
		||||
{
 | 
			
		||||
	"pools":[
 | 
			
		||||
	         {
 | 
			
		||||
			"lb_algorithm":"ROUND_ROBIN",
 | 
			
		||||
			"protocol":"HTTP",
 | 
			
		||||
			"description":"",
 | 
			
		||||
			"healthmonitor_id": "466c8345-28d8-4f84-a246-e04380b0461d",
 | 
			
		||||
			"members":[{"id": "53306cda-815d-4354-9fe4-59e09da9c3c5"}],
 | 
			
		||||
			"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
 | 
			
		||||
			"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
 | 
			
		||||
			"id":"72741b06-df4d-4715-b142-276b6bce75ab",
 | 
			
		||||
			"name":"web",
 | 
			
		||||
			"admin_state_up":true,
 | 
			
		||||
			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
			"provider": "haproxy"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"lb_algorithm":"LEAST_CONNECTION",
 | 
			
		||||
			"protocol":"HTTP",
 | 
			
		||||
			"description":"",
 | 
			
		||||
			"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
 | 
			
		||||
			"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
 | 
			
		||||
			"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
 | 
			
		||||
			"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
 | 
			
		||||
			"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
 | 
			
		||||
			"name":"db",
 | 
			
		||||
			"admin_state_up":true,
 | 
			
		||||
			"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
			"provider": "haproxy"
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// SinglePoolBody is the canned body of a Get request on an existing pool.
 | 
			
		||||
const SinglePoolBody = `
 | 
			
		||||
{
 | 
			
		||||
	"pool": {
 | 
			
		||||
		"lb_algorithm":"LEAST_CONNECTION",
 | 
			
		||||
		"protocol":"HTTP",
 | 
			
		||||
		"description":"",
 | 
			
		||||
		"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
 | 
			
		||||
		"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
 | 
			
		||||
		"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
 | 
			
		||||
		"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
 | 
			
		||||
		"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
 | 
			
		||||
		"name":"db",
 | 
			
		||||
		"admin_state_up":true,
 | 
			
		||||
		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
		"provider": "haproxy"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// PostUpdatePoolBody is the canned response body of a Update request on an existing pool.
 | 
			
		||||
const PostUpdatePoolBody = `
 | 
			
		||||
{
 | 
			
		||||
	"pool": {
 | 
			
		||||
		"lb_algorithm":"LEAST_CONNECTION",
 | 
			
		||||
		"protocol":"HTTP",
 | 
			
		||||
		"description":"",
 | 
			
		||||
		"healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d",
 | 
			
		||||
		"members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}],
 | 
			
		||||
		"listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}],
 | 
			
		||||
		"loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}],
 | 
			
		||||
		"id":"c3741b06-df4d-4715-b142-276b6bce75ab",
 | 
			
		||||
		"name":"db",
 | 
			
		||||
		"admin_state_up":true,
 | 
			
		||||
		"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
		"provider": "haproxy"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	PoolWeb = Pool{
 | 
			
		||||
		LBMethod:      "ROUND_ROBIN",
 | 
			
		||||
		Protocol:      "HTTP",
 | 
			
		||||
		Description:   "",
 | 
			
		||||
		MonitorID:     "466c8345-28d8-4f84-a246-e04380b0461d",
 | 
			
		||||
		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
		AdminStateUp:  true,
 | 
			
		||||
		Name:          "web",
 | 
			
		||||
		Members:       []Member{{ID: "53306cda-815d-4354-9fe4-59e09da9c3c5"}},
 | 
			
		||||
		ID:            "72741b06-df4d-4715-b142-276b6bce75ab",
 | 
			
		||||
		Loadbalancers: []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
 | 
			
		||||
		Listeners:     []ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
 | 
			
		||||
		Provider:      "haproxy",
 | 
			
		||||
	}
 | 
			
		||||
	PoolDb = Pool{
 | 
			
		||||
		LBMethod:      "LEAST_CONNECTION",
 | 
			
		||||
		Protocol:      "HTTP",
 | 
			
		||||
		Description:   "",
 | 
			
		||||
		MonitorID:     "5f6c8345-28d8-4f84-a246-e04380b0461d",
 | 
			
		||||
		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
		AdminStateUp:  true,
 | 
			
		||||
		Name:          "db",
 | 
			
		||||
		Members:       []Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}},
 | 
			
		||||
		ID:            "c3741b06-df4d-4715-b142-276b6bce75ab",
 | 
			
		||||
		Loadbalancers: []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
 | 
			
		||||
		Listeners:     []ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
 | 
			
		||||
		Provider:      "haproxy",
 | 
			
		||||
	}
 | 
			
		||||
	PoolUpdated = Pool{
 | 
			
		||||
		LBMethod:      "LEAST_CONNECTION",
 | 
			
		||||
		Protocol:      "HTTP",
 | 
			
		||||
		Description:   "",
 | 
			
		||||
		MonitorID:     "5f6c8345-28d8-4f84-a246-e04380b0461d",
 | 
			
		||||
		TenantID:      "83657cfcdfe44cd5920adaf26c48ceea",
 | 
			
		||||
		AdminStateUp:  true,
 | 
			
		||||
		Name:          "db",
 | 
			
		||||
		Members:       []Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}},
 | 
			
		||||
		ID:            "c3741b06-df4d-4715-b142-276b6bce75ab",
 | 
			
		||||
		Loadbalancers: []LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}},
 | 
			
		||||
		Listeners:     []ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}},
 | 
			
		||||
		Provider:      "haproxy",
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HandlePoolListSuccessfully sets up the test server to respond to a pool List request.
 | 
			
		||||
func HandlePoolListSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
		r.ParseForm()
 | 
			
		||||
		marker := r.Form.Get("marker")
 | 
			
		||||
		switch marker {
 | 
			
		||||
		case "":
 | 
			
		||||
			fmt.Fprintf(w, PoolsListBody)
 | 
			
		||||
		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
 | 
			
		||||
			fmt.Fprintf(w, `{ "pools": [] }`)
 | 
			
		||||
		default:
 | 
			
		||||
			t.Fatalf("/v2.0/lbaas/pools invoked with unexpected marker=[%s]", marker)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandlePoolCreationSuccessfully sets up the test server to respond to a pool creation request
 | 
			
		||||
// with a given response.
 | 
			
		||||
func HandlePoolCreationSuccessfully(t *testing.T, response string) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "POST")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestJSONRequest(t, r, `{
 | 
			
		||||
			"pool": {
 | 
			
		||||
			        "lb_algorithm": "ROUND_ROBIN",
 | 
			
		||||
			        "protocol": "HTTP",
 | 
			
		||||
			        "name": "Example pool",
 | 
			
		||||
			        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
 | 
			
		||||
			        "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab"
 | 
			
		||||
			}
 | 
			
		||||
		}`)
 | 
			
		||||
 | 
			
		||||
		w.WriteHeader(http.StatusAccepted)
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
		fmt.Fprintf(w, response)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandlePoolGetSuccessfully sets up the test server to respond to a pool Get request.
 | 
			
		||||
func HandlePoolGetSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestHeader(t, r, "Accept", "application/json")
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, SinglePoolBody)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandlePoolDeletionSuccessfully sets up the test server to respond to a pool deletion request.
 | 
			
		||||
func HandlePoolDeletionSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "DELETE")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.WriteHeader(http.StatusNoContent)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandlePoolUpdateSuccessfully sets up the test server to respond to a pool Update request.
 | 
			
		||||
func HandlePoolUpdateSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "PUT")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestHeader(t, r, "Accept", "application/json")
 | 
			
		||||
		th.TestHeader(t, r, "Content-Type", "application/json")
 | 
			
		||||
		th.TestJSONRequest(t, r, `{
 | 
			
		||||
			"pool": {
 | 
			
		||||
				"name": "NewPoolName",
 | 
			
		||||
                                "lb_algorithm": "LEAST_CONNECTIONS"
 | 
			
		||||
			}
 | 
			
		||||
		}`)
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, PostUpdatePoolBody)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MembersListBody contains the canned body of a member list response.
 | 
			
		||||
const MembersListBody = `
 | 
			
		||||
{
 | 
			
		||||
	"members":[
 | 
			
		||||
		{
 | 
			
		||||
			"id": "2a280670-c202-4b0b-a562-34077415aabf",
 | 
			
		||||
			"address": "10.0.2.10",
 | 
			
		||||
			"weight": 5,
 | 
			
		||||
			"name": "web",
 | 
			
		||||
			"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
 | 
			
		||||
			"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
 | 
			
		||||
			"admin_state_up":true,
 | 
			
		||||
			"protocol_port": 80
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
 | 
			
		||||
			"address": "10.0.2.11",
 | 
			
		||||
			"weight": 10,
 | 
			
		||||
			"name": "db",
 | 
			
		||||
			"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
 | 
			
		||||
			"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
 | 
			
		||||
			"admin_state_up":false,
 | 
			
		||||
			"protocol_port": 80
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// SingleMemberBody is the canned body of a Get request on an existing member.
 | 
			
		||||
const SingleMemberBody = `
 | 
			
		||||
{
 | 
			
		||||
	"member": {
 | 
			
		||||
		"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
 | 
			
		||||
		"address": "10.0.2.11",
 | 
			
		||||
		"weight": 10,
 | 
			
		||||
		"name": "db",
 | 
			
		||||
		"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
 | 
			
		||||
		"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
 | 
			
		||||
		"admin_state_up":false,
 | 
			
		||||
		"protocol_port": 80
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// PostUpdateMemberBody is the canned response body of a Update request on an existing member.
 | 
			
		||||
const PostUpdateMemberBody = `
 | 
			
		||||
{
 | 
			
		||||
	"member": {
 | 
			
		||||
		"id": "fad389a3-9a4a-4762-a365-8c7038508b5d",
 | 
			
		||||
		"address": "10.0.2.11",
 | 
			
		||||
		"weight": 10,
 | 
			
		||||
		"name": "db",
 | 
			
		||||
		"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
 | 
			
		||||
		"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
 | 
			
		||||
		"admin_state_up":false,
 | 
			
		||||
		"protocol_port": 80
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	MemberWeb = Member{
 | 
			
		||||
		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
 | 
			
		||||
		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
 | 
			
		||||
		AdminStateUp: true,
 | 
			
		||||
		Name:         "web",
 | 
			
		||||
		ID:           "2a280670-c202-4b0b-a562-34077415aabf",
 | 
			
		||||
		Address:      "10.0.2.10",
 | 
			
		||||
		Weight:       5,
 | 
			
		||||
		ProtocolPort: 80,
 | 
			
		||||
	}
 | 
			
		||||
	MemberDb = Member{
 | 
			
		||||
		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
 | 
			
		||||
		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
 | 
			
		||||
		AdminStateUp: false,
 | 
			
		||||
		Name:         "db",
 | 
			
		||||
		ID:           "fad389a3-9a4a-4762-a365-8c7038508b5d",
 | 
			
		||||
		Address:      "10.0.2.11",
 | 
			
		||||
		Weight:       10,
 | 
			
		||||
		ProtocolPort: 80,
 | 
			
		||||
	}
 | 
			
		||||
	MemberUpdated = Member{
 | 
			
		||||
		SubnetID:     "1981f108-3c48-48d2-b908-30f7d28532c9",
 | 
			
		||||
		TenantID:     "2ffc6e22aae24e4795f87155d24c896f",
 | 
			
		||||
		AdminStateUp: false,
 | 
			
		||||
		Name:         "db",
 | 
			
		||||
		ID:           "fad389a3-9a4a-4762-a365-8c7038508b5d",
 | 
			
		||||
		Address:      "10.0.2.11",
 | 
			
		||||
		Weight:       10,
 | 
			
		||||
		ProtocolPort: 80,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HandleMemberListSuccessfully sets up the test server to respond to a member List request.
 | 
			
		||||
func HandleMemberListSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
		r.ParseForm()
 | 
			
		||||
		marker := r.Form.Get("marker")
 | 
			
		||||
		switch marker {
 | 
			
		||||
		case "":
 | 
			
		||||
			fmt.Fprintf(w, MembersListBody)
 | 
			
		||||
		case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab":
 | 
			
		||||
			fmt.Fprintf(w, `{ "members": [] }`)
 | 
			
		||||
		default:
 | 
			
		||||
			t.Fatalf("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members invoked with unexpected marker=[%s]", marker)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleMemberCreationSuccessfully sets up the test server to respond to a member creation request
 | 
			
		||||
// with a given response.
 | 
			
		||||
func HandleMemberCreationSuccessfully(t *testing.T, response string) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "POST")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestJSONRequest(t, r, `{
 | 
			
		||||
			"member": {
 | 
			
		||||
			        "address": "10.0.2.11",
 | 
			
		||||
			        "weight": 10,
 | 
			
		||||
			        "name": "db",
 | 
			
		||||
			        "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
 | 
			
		||||
			        "tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
 | 
			
		||||
			        "protocol_port": 80
 | 
			
		||||
			}
 | 
			
		||||
		}`)
 | 
			
		||||
 | 
			
		||||
		w.WriteHeader(http.StatusAccepted)
 | 
			
		||||
		w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
		fmt.Fprintf(w, response)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleMemberGetSuccessfully sets up the test server to respond to a member Get request.
 | 
			
		||||
func HandleMemberGetSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "GET")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestHeader(t, r, "Accept", "application/json")
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, SingleMemberBody)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleMemberDeletionSuccessfully sets up the test server to respond to a member deletion request.
 | 
			
		||||
func HandleMemberDeletionSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "DELETE")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
 | 
			
		||||
		w.WriteHeader(http.StatusNoContent)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleMemberUpdateSuccessfully sets up the test server to respond to a member Update request.
 | 
			
		||||
func HandleMemberUpdateSuccessfully(t *testing.T) {
 | 
			
		||||
	th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		th.TestMethod(t, r, "PUT")
 | 
			
		||||
		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 | 
			
		||||
		th.TestHeader(t, r, "Accept", "application/json")
 | 
			
		||||
		th.TestHeader(t, r, "Content-Type", "application/json")
 | 
			
		||||
		th.TestJSONRequest(t, r, `{
 | 
			
		||||
			"member": {
 | 
			
		||||
				"name": "newMemberName",
 | 
			
		||||
                                "weight": 4
 | 
			
		||||
			}
 | 
			
		||||
		}`)
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintf(w, PostUpdateMemberBody)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@@ -1,485 +0,0 @@
 | 
			
		||||
package pools
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AdminState gives users a solid type to work with for create and update
 | 
			
		||||
// operations. It is recommended that users use the `Up` and `Down` enums.
 | 
			
		||||
type AdminState *bool
 | 
			
		||||
 | 
			
		||||
type poolOpts struct {
 | 
			
		||||
	// Only required if the caller has an admin role and wants to create a pool
 | 
			
		||||
	// for another tenant.
 | 
			
		||||
	TenantID string
 | 
			
		||||
 | 
			
		||||
	// Optional. Name of the pool.
 | 
			
		||||
	Name string
 | 
			
		||||
 | 
			
		||||
	// Optional. Human-readable description for the pool.
 | 
			
		||||
	Description string
 | 
			
		||||
 | 
			
		||||
	// Required. The protocol used by the pool members, you can use either
 | 
			
		||||
	// ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS.
 | 
			
		||||
	Protocol Protocol
 | 
			
		||||
 | 
			
		||||
	// The Loadbalancer on which the members of the pool will be associated with.
 | 
			
		||||
	// Note:  one of LoadbalancerID or ListenerID must be provided.
 | 
			
		||||
	LoadbalancerID string
 | 
			
		||||
 | 
			
		||||
	// The Listener on which the members of the pool will be associated with.
 | 
			
		||||
	// Note:  one of LoadbalancerID or ListenerID must be provided.
 | 
			
		||||
	ListenerID string
 | 
			
		||||
 | 
			
		||||
	// Required. The algorithm used to distribute load between the members of the pool. The
 | 
			
		||||
	// current specification supports LBMethodRoundRobin, LBMethodLeastConnections
 | 
			
		||||
	// and LBMethodSourceIp as valid values for this attribute.
 | 
			
		||||
	LBMethod LBMethod
 | 
			
		||||
 | 
			
		||||
	// Optional. Omit this field to prevent session persistence.
 | 
			
		||||
	Persistence *SessionPersistence
 | 
			
		||||
 | 
			
		||||
	// Optional. The administrative state of the Pool. A valid value is true (UP)
 | 
			
		||||
	// or false (DOWN).
 | 
			
		||||
	AdminStateUp *bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convenience vars for AdminStateUp values.
 | 
			
		||||
var (
 | 
			
		||||
	iTrue  = true
 | 
			
		||||
	iFalse = false
 | 
			
		||||
 | 
			
		||||
	Up   AdminState = &iTrue
 | 
			
		||||
	Down AdminState = &iFalse
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
// sort by a particular Pool attribute. SortDir sets the direction, and is
 | 
			
		||||
// either `asc' or `desc'. Marker and Limit are used for pagination.
 | 
			
		||||
type ListOpts struct {
 | 
			
		||||
	LBMethod       string `q:"lb_algorithm"`
 | 
			
		||||
	Protocol       string `q:"protocol"`
 | 
			
		||||
	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 ListOptsBuilder) pagination.Pager {
 | 
			
		||||
	url := rootURL(c)
 | 
			
		||||
	if opts != nil {
 | 
			
		||||
		query, err := opts.ToPoolListQuery()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return pagination.Pager{Err: err}
 | 
			
		||||
		}
 | 
			
		||||
		url += query
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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       LBMethod = "ROUND_ROBIN"
 | 
			
		||||
	LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS"
 | 
			
		||||
	LBMethodSourceIp         LBMethod = "SOURCE_IP"
 | 
			
		||||
 | 
			
		||||
	ProtocolTCP   Protocol = "TCP"
 | 
			
		||||
	ProtocolHTTP  Protocol = "HTTP"
 | 
			
		||||
	ProtocolHTTPS Protocol = "HTTPS"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	errLoadbalancerOrListenerRequired = fmt.Errorf("A ListenerID or LoadbalancerID is required")
 | 
			
		||||
	errValidLBMethodRequired          = fmt.Errorf("A valid LBMethod is required. Supported values are ROUND_ROBIN, LEAST_CONNECTIONS, SOURCE_IP")
 | 
			
		||||
	errValidProtocolRequired          = fmt.Errorf("A valid Protocol is required. Supported values are TCP, HTTP, HTTPS")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CreateOptsBuilder is the interface options structs have to satisfy in order
 | 
			
		||||
// to be used in the main Create operation in this package. Since many
 | 
			
		||||
// extensions decorate or modify the common logic, it is useful for them to
 | 
			
		||||
// satisfy a basic interface in order for them to be used.
 | 
			
		||||
type CreateOptsBuilder interface {
 | 
			
		||||
	ToPoolCreateMap() (map[string]interface{}, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateOpts is the common options struct used in this package's Create
 | 
			
		||||
// operation.
 | 
			
		||||
type CreateOpts poolOpts
 | 
			
		||||
 | 
			
		||||
// ToPoolCreateMap casts a CreateOpts struct to a map.
 | 
			
		||||
func (opts CreateOpts) ToPoolCreateMap() (map[string]interface{}, error) {
 | 
			
		||||
	l := make(map[string]interface{})
 | 
			
		||||
	allowedLBMethod := map[LBMethod]bool{LBMethodRoundRobin: true, LBMethodLeastConnections: true, LBMethodSourceIp: true}
 | 
			
		||||
	allowedProtocol := map[Protocol]bool{ProtocolTCP: true, ProtocolHTTP: true, ProtocolHTTPS: true}
 | 
			
		||||
 | 
			
		||||
	if allowedLBMethod[opts.LBMethod] {
 | 
			
		||||
		l["lb_algorithm"] = opts.LBMethod
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, errValidLBMethodRequired
 | 
			
		||||
	}
 | 
			
		||||
	if allowedProtocol[opts.Protocol] {
 | 
			
		||||
		l["protocol"] = opts.Protocol
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, errValidProtocolRequired
 | 
			
		||||
	}
 | 
			
		||||
	if opts.LoadbalancerID == "" && opts.ListenerID == "" {
 | 
			
		||||
		return nil, errLoadbalancerOrListenerRequired
 | 
			
		||||
	} else {
 | 
			
		||||
		if opts.LoadbalancerID != "" {
 | 
			
		||||
			l["loadbalancer_id"] = opts.LoadbalancerID
 | 
			
		||||
		}
 | 
			
		||||
		if opts.ListenerID != "" {
 | 
			
		||||
			l["listener_id"] = opts.ListenerID
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if opts.AdminStateUp != nil {
 | 
			
		||||
		l["admin_state_up"] = &opts.AdminStateUp
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Name != "" {
 | 
			
		||||
		l["name"] = opts.Name
 | 
			
		||||
	}
 | 
			
		||||
	if opts.TenantID != "" {
 | 
			
		||||
		l["tenant_id"] = opts.TenantID
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Persistence != nil {
 | 
			
		||||
		l["session_persistence"] = &opts.Persistence
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return map[string]interface{}{"pool": l}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create accepts a CreateOpts struct and uses the values to create a new
 | 
			
		||||
// load balancer pool.
 | 
			
		||||
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 | 
			
		||||
	var res CreateResult
 | 
			
		||||
 | 
			
		||||
	reqBody, err := opts.ToPoolCreateMap()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		res.Err = err
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Send request to API
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular pool based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateOpts is the common options struct used in this package's Update
 | 
			
		||||
// operation.
 | 
			
		||||
type UpdateOpts poolOpts
 | 
			
		||||
 | 
			
		||||
// ToPoolUpdateMap casts a UpdateOpts struct to a map.
 | 
			
		||||
func (opts UpdateOpts) ToPoolUpdateMap() (map[string]interface{}, error) {
 | 
			
		||||
	l := make(map[string]interface{})
 | 
			
		||||
	allowedLBMethod := map[LBMethod]bool{LBMethodRoundRobin: true, LBMethodLeastConnections: true, LBMethodSourceIp: true}
 | 
			
		||||
 | 
			
		||||
	if opts.LBMethod != "" {
 | 
			
		||||
		if allowedLBMethod[opts.LBMethod] {
 | 
			
		||||
			l["lb_algorithm"] = opts.LBMethod
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil, errValidLBMethodRequired
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Name != "" {
 | 
			
		||||
		l["name"] = opts.Name
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Description != "" {
 | 
			
		||||
		l["description"] = opts.Description
 | 
			
		||||
	}
 | 
			
		||||
	if opts.AdminStateUp != nil {
 | 
			
		||||
		l["admin_state_up"] = &opts.AdminStateUp
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return map[string]interface{}{"pool": l}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update allows pools to be updated.
 | 
			
		||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
 | 
			
		||||
	reqBody, err := opts.ToPoolUpdateMap()
 | 
			
		||||
	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},
 | 
			
		||||
	})
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete will permanently delete a particular pool based on its unique ID.
 | 
			
		||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateOpts contains all the values needed to create a new Member for a Pool.
 | 
			
		||||
type memberOpts struct {
 | 
			
		||||
	// Optional. Name of the Member.
 | 
			
		||||
	Name string
 | 
			
		||||
 | 
			
		||||
	// Only required if the caller has an admin role and wants to create a Member
 | 
			
		||||
	// for another tenant.
 | 
			
		||||
	TenantID string
 | 
			
		||||
 | 
			
		||||
	// Required. The IP address of the member to receive traffic from the load balancer.
 | 
			
		||||
	Address string
 | 
			
		||||
 | 
			
		||||
	// Required. The port on which to listen for client traffic.
 | 
			
		||||
	ProtocolPort int
 | 
			
		||||
 | 
			
		||||
	// Optional. A positive integer value that indicates the relative portion of
 | 
			
		||||
	// traffic that this member should receive from the pool. For example, a
 | 
			
		||||
	// member with a weight of 10 receives five times as much traffic as a member
 | 
			
		||||
	// with a weight of 2.
 | 
			
		||||
	Weight int
 | 
			
		||||
 | 
			
		||||
	// Optional.  If you omit this parameter, LBaaS uses the vip_subnet_id
 | 
			
		||||
	// parameter value for the subnet UUID.
 | 
			
		||||
	SubnetID string
 | 
			
		||||
 | 
			
		||||
	// Optional. The administrative state of the Pool. A valid value is true (UP)
 | 
			
		||||
	// or false (DOWN).
 | 
			
		||||
	AdminStateUp *bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular Pool Member based on its unique ID.
 | 
			
		||||
func GetAssociateMember(c *gophercloud.ServiceClient, poolID string, memberID string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(memberResourceURL(c, poolID, memberID), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateOpts is the common options struct used in this package's Update
 | 
			
		||||
// operation.
 | 
			
		||||
type MemberUpdateOpts memberOpts
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
 | 
			
		||||
	reqBody, err := opts.ToMemberUpdateMap()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		res.Err = err
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Send request to API
 | 
			
		||||
	_, res.Err = c.Put(memberResourceURL(c, poolID, memberID), reqBody, &res.Body, &gophercloud.RequestOpts{
 | 
			
		||||
		OkCodes: []int{200, 201, 202},
 | 
			
		||||
	})
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DisassociateMember will remove and disassociate a Member from a particular Pool.
 | 
			
		||||
func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(memberResourceURL(c, poolID, memberID), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
@@ -1,274 +0,0 @@
 | 
			
		||||
package pools
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SessionPersistence represents the session persistence feature of the load
 | 
			
		||||
// balancing service. It attempts to force connections or requests in the same
 | 
			
		||||
// session to be processed by the same member as long as it is ative. Three
 | 
			
		||||
// types of persistence are supported:
 | 
			
		||||
//
 | 
			
		||||
// SOURCE_IP:   With this mode, all connections originating from the same source
 | 
			
		||||
//              IP address, will be handled by the same Member of the Pool.
 | 
			
		||||
// HTTP_COOKIE: With this persistence mode, the load balancing function will
 | 
			
		||||
//              create a cookie on the first request from a client. Subsequent
 | 
			
		||||
//              requests containing the same cookie value will be handled by
 | 
			
		||||
//              the same Member of the Pool.
 | 
			
		||||
// APP_COOKIE:  With this persistence mode, the load balancing function will
 | 
			
		||||
//              rely on a cookie established by the backend application. All
 | 
			
		||||
//              requests carrying the same cookie value will be handled by the
 | 
			
		||||
//              same Member of the Pool.
 | 
			
		||||
type SessionPersistence struct {
 | 
			
		||||
	// The type of persistence mode
 | 
			
		||||
	Type string `mapstructure:"type" json:"type"`
 | 
			
		||||
 | 
			
		||||
	// Name of cookie if persistence mode is set appropriately
 | 
			
		||||
	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
 | 
			
		||||
// method to handle the new requests or connections received on the VIP address.
 | 
			
		||||
type Pool struct {
 | 
			
		||||
	// The load-balancer algorithm, which is round-robin, least-connections, and
 | 
			
		||||
	// so on. This value, which must be supported, is dependent on the provider.
 | 
			
		||||
	// Round-robin must be supported.
 | 
			
		||||
	LBMethod string `json:"lb_algorithm" mapstructure:"lb_algorithm"`
 | 
			
		||||
 | 
			
		||||
	// The protocol of the Pool, which is TCP, HTTP, or HTTPS.
 | 
			
		||||
	Protocol string
 | 
			
		||||
 | 
			
		||||
	// Description for the Pool.
 | 
			
		||||
	Description string
 | 
			
		||||
 | 
			
		||||
	// A list of listeners objects IDs.
 | 
			
		||||
	Listeners []ListenerID `mapstructure:"listeners" json:"listeners"` //[]map[string]interface{}
 | 
			
		||||
 | 
			
		||||
	// A list of member objects IDs.
 | 
			
		||||
	Members []Member `mapstructure:"members" json:"members"`
 | 
			
		||||
 | 
			
		||||
	// 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.
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
	// The unique ID for the Pool.
 | 
			
		||||
	ID string
 | 
			
		||||
 | 
			
		||||
	// A list of load balancer objects IDs.
 | 
			
		||||
	Loadbalancers []LoadBalancerID `mapstructure:"loadbalancers" json:"loadbalancers"`
 | 
			
		||||
 | 
			
		||||
	// Indicates whether connections in the same session will be processed by the
 | 
			
		||||
	// same Pool member or not.
 | 
			
		||||
	Persistence SessionPersistence `mapstructure:"session_persistence" json:"session_persistence"`
 | 
			
		||||
 | 
			
		||||
	// The provider
 | 
			
		||||
	Provider string
 | 
			
		||||
 | 
			
		||||
	Monitor monitors.Monitor `mapstructure:"healthmonitor" json:"healthmonitor"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"pools_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a PoolPage struct is empty.
 | 
			
		||||
func (p PoolPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractPools(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractPools accepts a Page struct, specifically a RouterPage struct,
 | 
			
		||||
// and extracts the elements into a slice of Router structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractPools(page pagination.Page) ([]Pool, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		Pools []Pool `mapstructure:"pools" json:"pools"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(PoolPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.Pools, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract is a function that accepts a result and extracts a router.
 | 
			
		||||
func (r commonResult) Extract() (*Pool, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		Pool *Pool `json:"pool"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.Pool, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Member represents the application running on a backend server.
 | 
			
		||||
type Member struct {
 | 
			
		||||
	// Name of the Member.
 | 
			
		||||
	Name string `json:"name" mapstructure:"name"`
 | 
			
		||||
 | 
			
		||||
	// Weight of Member.
 | 
			
		||||
	Weight int `json:"weight" mapstructure:"weight"`
 | 
			
		||||
 | 
			
		||||
	// The administrative state of the member, which is up (true) or down (false).
 | 
			
		||||
	AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
 | 
			
		||||
 | 
			
		||||
	// Owner of the Member. Only an administrative user can specify a tenant ID
 | 
			
		||||
	// other than its own.
 | 
			
		||||
	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
 | 
			
		||||
 | 
			
		||||
	// parameter value for the subnet UUID.
 | 
			
		||||
	SubnetID string `json:"subnet_id" mapstructure:"subnet_id"`
 | 
			
		||||
 | 
			
		||||
	// The Pool to which the Member belongs.
 | 
			
		||||
	PoolID string `json:"pool_id" mapstructure:"pool_id"`
 | 
			
		||||
 | 
			
		||||
	// The IP address of the Member.
 | 
			
		||||
	Address string `json:"address" mapstructure:"address"`
 | 
			
		||||
 | 
			
		||||
	// The port on which the application is hosted.
 | 
			
		||||
	ProtocolPort int `json:"protocol_port" mapstructure:"protocol_port"`
 | 
			
		||||
 | 
			
		||||
	// The unique ID for the Member.
 | 
			
		||||
	ID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MemberPage is the page returned by a pager when traversing over a
 | 
			
		||||
// collection of Members in a Pool.
 | 
			
		||||
type MemberPage struct {
 | 
			
		||||
	pagination.LinkedPageBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextPageURL is invoked when a paginated collection of members has reached
 | 
			
		||||
// the end of a page and the pager seeks to traverse over a new one. In order
 | 
			
		||||
// to do this, it needs to construct the next page's URL.
 | 
			
		||||
func (p MemberPage) NextPageURL() (string, error) {
 | 
			
		||||
	type resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"members_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a MemberPage struct is empty.
 | 
			
		||||
func (p MemberPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractMembers(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractMembers accepts a Page struct, specifically a RouterPage struct,
 | 
			
		||||
// and extracts the elements into a slice of Router structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractMembers(page pagination.Page) ([]Member, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		Member []Member `mapstructure:"members" json:"members"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(MemberPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.Member, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractMember is a function that accepts a result and extracts a router.
 | 
			
		||||
func (r commonResult) ExtractMember() (*Member, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		Member *Member `json:"member"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.Member, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateResult represents the result of a create operation.
 | 
			
		||||
type CreateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateResult represents the result of an update operation.
 | 
			
		||||
type UpdateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AssociateResult represents the result of an association operation.
 | 
			
		||||
type AssociateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
@@ -1,25 +0,0 @@
 | 
			
		||||
package pools
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rootPath     = "lbaas"
 | 
			
		||||
	resourcePath = "pools"
 | 
			
		||||
	memberPath   = "members"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath, id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func memberRootURL(c *gophercloud.ServiceClient, poolId string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath, poolId, memberPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func memberResourceURL(c *gophercloud.ServiceClient, poolID string, memeberID string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memeberID)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,131 +0,0 @@
 | 
			
		||||
package groups
 | 
			
		||||
 | 
			
		||||
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 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"`
 | 
			
		||||
	TenantID string `q:"tenant_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
 | 
			
		||||
// security groups. It accepts a ListOpts struct, which allows you to filter
 | 
			
		||||
// and sort the returned collection for greater efficiency.
 | 
			
		||||
func List(c *gophercloud.ServiceClient, opts 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 SecGroupPage{pagination.LinkedPageBase{PageResult: r}}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	errNameRequired = fmt.Errorf("Name is required")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CreateOpts contains all the values needed to create a new security group.
 | 
			
		||||
type CreateOpts struct {
 | 
			
		||||
	// Required. Human-readable name for the VIP. Does not have to be unique.
 | 
			
		||||
	Name string
 | 
			
		||||
 | 
			
		||||
	// Required for admins. Indicates the owner of the VIP.
 | 
			
		||||
	TenantID string
 | 
			
		||||
 | 
			
		||||
	// Optional. Describes the security group.
 | 
			
		||||
	Description string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create is an operation which provisions a new security group with default
 | 
			
		||||
// security group rules for the IPv4 and IPv6 ether types.
 | 
			
		||||
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 | 
			
		||||
	var res CreateResult
 | 
			
		||||
 | 
			
		||||
	// Validate required opts
 | 
			
		||||
	if opts.Name == "" {
 | 
			
		||||
		res.Err = errNameRequired
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type secgroup struct {
 | 
			
		||||
		Name        string `json:"name"`
 | 
			
		||||
		TenantID    string `json:"tenant_id,omitempty"`
 | 
			
		||||
		Description string `json:"description,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type request struct {
 | 
			
		||||
		SecGroup secgroup `json:"security_group"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{SecGroup: secgroup{
 | 
			
		||||
		Name:        opts.Name,
 | 
			
		||||
		TenantID:    opts.TenantID,
 | 
			
		||||
		Description: opts.Description,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular security group based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete will permanently delete a particular security group based on its unique ID.
 | 
			
		||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IDFromName is a convenience function that returns a security group's ID given its name.
 | 
			
		||||
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
 | 
			
		||||
	securityGroupCount := 0
 | 
			
		||||
	securityGroupID := ""
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return "", fmt.Errorf("A security group name must be provided.")
 | 
			
		||||
	}
 | 
			
		||||
	pager := List(client, ListOpts{})
 | 
			
		||||
	pager.EachPage(func(page pagination.Page) (bool, error) {
 | 
			
		||||
		securityGroupList, err := ExtractGroups(page)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, s := range securityGroupList {
 | 
			
		||||
			if s.Name == name {
 | 
			
		||||
				securityGroupCount++
 | 
			
		||||
				securityGroupID = s.ID
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return true, nil
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	switch securityGroupCount {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return "", fmt.Errorf("Unable to find security group: %s", name)
 | 
			
		||||
	case 1:
 | 
			
		||||
		return securityGroupID, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return "", fmt.Errorf("Found %d security groups matching %s", securityGroupCount, name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,108 +0,0 @@
 | 
			
		||||
package groups
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SecGroup represents a container for security group rules.
 | 
			
		||||
type SecGroup struct {
 | 
			
		||||
	// The UUID for the security group.
 | 
			
		||||
	ID string
 | 
			
		||||
 | 
			
		||||
	// Human-readable name for the security group. Might not be unique. Cannot be
 | 
			
		||||
	// named "default" as that is automatically created for a tenant.
 | 
			
		||||
	Name string
 | 
			
		||||
 | 
			
		||||
	// The security group description.
 | 
			
		||||
	Description string
 | 
			
		||||
 | 
			
		||||
	// A slice of security group rules that dictate the permitted behaviour for
 | 
			
		||||
	// traffic entering and leaving the group.
 | 
			
		||||
	Rules []rules.SecGroupRule `json:"security_group_rules" mapstructure:"security_group_rules"`
 | 
			
		||||
 | 
			
		||||
	// Owner of the security group. Only admin users can specify a TenantID
 | 
			
		||||
	// other than their own.
 | 
			
		||||
	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SecGroupPage is the page returned by a pager when traversing over a
 | 
			
		||||
// collection of security groups.
 | 
			
		||||
type SecGroupPage struct {
 | 
			
		||||
	pagination.LinkedPageBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextPageURL is invoked when a paginated collection of security groups has
 | 
			
		||||
// reached the end of a page and the pager seeks to traverse over a new one. In
 | 
			
		||||
// order to do this, it needs to construct the next page's URL.
 | 
			
		||||
func (p SecGroupPage) NextPageURL() (string, error) {
 | 
			
		||||
	type resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"security_groups_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a SecGroupPage struct is empty.
 | 
			
		||||
func (p SecGroupPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractGroups(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractGroups accepts a Page struct, specifically a SecGroupPage struct,
 | 
			
		||||
// and extracts the elements into a slice of SecGroup structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractGroups(page pagination.Page) ([]SecGroup, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		SecGroups []SecGroup `mapstructure:"security_groups" json:"security_groups"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(SecGroupPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.SecGroups, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract is a function that accepts a result and extracts a security group.
 | 
			
		||||
func (r commonResult) Extract() (*SecGroup, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		SecGroup *SecGroup `mapstructure:"security_group" json:"security_group"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.SecGroup, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateResult represents the result of a create operation.
 | 
			
		||||
type CreateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
package groups
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const rootPath = "security-groups"
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(rootPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, id)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,174 +0,0 @@
 | 
			
		||||
package rules
 | 
			
		||||
 | 
			
		||||
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 security group 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 {
 | 
			
		||||
	Direction      string `q:"direction"`
 | 
			
		||||
	EtherType      string `q:"ethertype"`
 | 
			
		||||
	ID             string `q:"id"`
 | 
			
		||||
	PortRangeMax   int    `q:"port_range_max"`
 | 
			
		||||
	PortRangeMin   int    `q:"port_range_min"`
 | 
			
		||||
	Protocol       string `q:"protocol"`
 | 
			
		||||
	RemoteGroupID  string `q:"remote_group_id"`
 | 
			
		||||
	RemoteIPPrefix string `q:"remote_ip_prefix"`
 | 
			
		||||
	SecGroupID     string `q:"security_group_id"`
 | 
			
		||||
	TenantID       string `q:"tenant_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
 | 
			
		||||
// security group rules. It accepts a ListOpts struct, which allows you to filter
 | 
			
		||||
// and sort the returned collection for greater efficiency.
 | 
			
		||||
func List(c *gophercloud.ServiceClient, opts 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 SecGroupRulePage{pagination.LinkedPageBase{PageResult: r}}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Errors
 | 
			
		||||
var (
 | 
			
		||||
	errValidDirectionRequired = fmt.Errorf("A valid Direction is required")
 | 
			
		||||
	errValidEtherTypeRequired = fmt.Errorf("A valid EtherType is required")
 | 
			
		||||
	errSecGroupIDRequired     = fmt.Errorf("A valid SecGroupID is required")
 | 
			
		||||
	errValidProtocolRequired  = fmt.Errorf("A valid Protocol is required")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Constants useful for CreateOpts
 | 
			
		||||
const (
 | 
			
		||||
	DirIngress   = "ingress"
 | 
			
		||||
	DirEgress    = "egress"
 | 
			
		||||
	Ether4       = "IPv4"
 | 
			
		||||
	Ether6       = "IPv6"
 | 
			
		||||
	ProtocolTCP  = "tcp"
 | 
			
		||||
	ProtocolUDP  = "udp"
 | 
			
		||||
	ProtocolICMP = "icmp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CreateOpts contains all the values needed to create a new security group rule.
 | 
			
		||||
type CreateOpts struct {
 | 
			
		||||
	// Required. Must be either "ingress" or "egress": the direction in which the
 | 
			
		||||
	// security group rule is applied.
 | 
			
		||||
	Direction string
 | 
			
		||||
 | 
			
		||||
	// Required. Must be "IPv4" or "IPv6", and addresses represented in CIDR must
 | 
			
		||||
	// match the ingress or egress rules.
 | 
			
		||||
	EtherType string
 | 
			
		||||
 | 
			
		||||
	// Required. The security group ID to associate with this security group rule.
 | 
			
		||||
	SecGroupID string
 | 
			
		||||
 | 
			
		||||
	// Optional. The maximum port number in the range that is matched by the
 | 
			
		||||
	// security group rule. The PortRangeMin attribute constrains the PortRangeMax
 | 
			
		||||
	// attribute. If the protocol is ICMP, this value must be an ICMP type.
 | 
			
		||||
	PortRangeMax int
 | 
			
		||||
 | 
			
		||||
	// Optional. The minimum port number in the range that is matched by the
 | 
			
		||||
	// security group rule. If the protocol is TCP or UDP, this value must be
 | 
			
		||||
	// less than or equal to the value of the PortRangeMax attribute. If the
 | 
			
		||||
	// protocol is ICMP, this value must be an ICMP type.
 | 
			
		||||
	PortRangeMin int
 | 
			
		||||
 | 
			
		||||
	// Optional. The protocol that is matched by the security group rule. Valid
 | 
			
		||||
	// values are "tcp", "udp", "icmp" or an empty string.
 | 
			
		||||
	Protocol string
 | 
			
		||||
 | 
			
		||||
	// Optional. The remote group ID to be associated with this security group
 | 
			
		||||
	// rule. You can specify either RemoteGroupID or RemoteIPPrefix.
 | 
			
		||||
	RemoteGroupID string
 | 
			
		||||
 | 
			
		||||
	// Optional. The remote IP prefix to be associated with this security group
 | 
			
		||||
	// rule. You can specify either RemoteGroupID or RemoteIPPrefix. This
 | 
			
		||||
	// attribute matches the specified IP prefix as the source IP address of the
 | 
			
		||||
	// IP packet.
 | 
			
		||||
	RemoteIPPrefix string
 | 
			
		||||
 | 
			
		||||
	// Required for admins. Indicates the owner of the VIP.
 | 
			
		||||
	TenantID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create is an operation which adds a new security group rule and associates it
 | 
			
		||||
// with an existing security group (whose ID is specified in CreateOpts).
 | 
			
		||||
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 | 
			
		||||
	var res CreateResult
 | 
			
		||||
 | 
			
		||||
	// Validate required opts
 | 
			
		||||
	if opts.Direction != DirIngress && opts.Direction != DirEgress {
 | 
			
		||||
		res.Err = errValidDirectionRequired
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
	if opts.EtherType != Ether4 && opts.EtherType != Ether6 {
 | 
			
		||||
		res.Err = errValidEtherTypeRequired
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
	if opts.SecGroupID == "" {
 | 
			
		||||
		res.Err = errSecGroupIDRequired
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Protocol != "" && opts.Protocol != ProtocolTCP && opts.Protocol != ProtocolUDP && opts.Protocol != ProtocolICMP {
 | 
			
		||||
		res.Err = errValidProtocolRequired
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type secrule struct {
 | 
			
		||||
		Direction      string `json:"direction"`
 | 
			
		||||
		EtherType      string `json:"ethertype"`
 | 
			
		||||
		SecGroupID     string `json:"security_group_id"`
 | 
			
		||||
		PortRangeMax   int    `json:"port_range_max,omitempty"`
 | 
			
		||||
		PortRangeMin   int    `json:"port_range_min,omitempty"`
 | 
			
		||||
		Protocol       string `json:"protocol,omitempty"`
 | 
			
		||||
		RemoteGroupID  string `json:"remote_group_id,omitempty"`
 | 
			
		||||
		RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"`
 | 
			
		||||
		TenantID       string `json:"tenant_id,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type request struct {
 | 
			
		||||
		SecRule secrule `json:"security_group_rule"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqBody := request{SecRule: secrule{
 | 
			
		||||
		Direction:      opts.Direction,
 | 
			
		||||
		EtherType:      opts.EtherType,
 | 
			
		||||
		SecGroupID:     opts.SecGroupID,
 | 
			
		||||
		PortRangeMax:   opts.PortRangeMax,
 | 
			
		||||
		PortRangeMin:   opts.PortRangeMin,
 | 
			
		||||
		Protocol:       opts.Protocol,
 | 
			
		||||
		RemoteGroupID:  opts.RemoteGroupID,
 | 
			
		||||
		RemoteIPPrefix: opts.RemoteIPPrefix,
 | 
			
		||||
		TenantID:       opts.TenantID,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a particular security group rule based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete will permanently delete a particular security group rule based on its unique ID.
 | 
			
		||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(resourceURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
@@ -1,133 +0,0 @@
 | 
			
		||||
package rules
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SecGroupRule represents a rule to dictate the behaviour of incoming or
 | 
			
		||||
// outgoing traffic for a particular security group.
 | 
			
		||||
type SecGroupRule struct {
 | 
			
		||||
	// The UUID for this security group rule.
 | 
			
		||||
	ID string
 | 
			
		||||
 | 
			
		||||
	// The direction in which the security group rule is applied. The only values
 | 
			
		||||
	// allowed are "ingress" or "egress". For a compute instance, an ingress
 | 
			
		||||
	// security group rule is applied to incoming (ingress) traffic for that
 | 
			
		||||
	// instance. An egress rule is applied to traffic leaving the instance.
 | 
			
		||||
	Direction string
 | 
			
		||||
 | 
			
		||||
	// Must be IPv4 or IPv6, and addresses represented in CIDR must match the
 | 
			
		||||
	// ingress or egress rules.
 | 
			
		||||
	EtherType string `json:"ethertype" mapstructure:"ethertype"`
 | 
			
		||||
 | 
			
		||||
	// The security group ID to associate with this security group rule.
 | 
			
		||||
	SecGroupID string `json:"security_group_id" mapstructure:"security_group_id"`
 | 
			
		||||
 | 
			
		||||
	// The minimum port number in the range that is matched by the security group
 | 
			
		||||
	// rule. If the protocol is TCP or UDP, this value must be less than or equal
 | 
			
		||||
	// to the value of the PortRangeMax attribute. If the protocol is ICMP, this
 | 
			
		||||
	// value must be an ICMP type.
 | 
			
		||||
	PortRangeMin int `json:"port_range_min" mapstructure:"port_range_min"`
 | 
			
		||||
 | 
			
		||||
	// The maximum port number in the range that is matched by the security group
 | 
			
		||||
	// rule. The PortRangeMin attribute constrains the PortRangeMax attribute. If
 | 
			
		||||
	// the protocol is ICMP, this value must be an ICMP type.
 | 
			
		||||
	PortRangeMax int `json:"port_range_max" mapstructure:"port_range_max"`
 | 
			
		||||
 | 
			
		||||
	// The protocol that is matched by the security group rule. Valid values are
 | 
			
		||||
	// "tcp", "udp", "icmp" or an empty string.
 | 
			
		||||
	Protocol string
 | 
			
		||||
 | 
			
		||||
	// The remote group ID to be associated with this security group rule. You
 | 
			
		||||
	// can specify either RemoteGroupID or RemoteIPPrefix.
 | 
			
		||||
	RemoteGroupID string `json:"remote_group_id" mapstructure:"remote_group_id"`
 | 
			
		||||
 | 
			
		||||
	// The remote IP prefix to be associated with this security group rule. You
 | 
			
		||||
	// can specify either RemoteGroupID or RemoteIPPrefix . This attribute
 | 
			
		||||
	// matches the specified IP prefix as the source IP address of the IP packet.
 | 
			
		||||
	RemoteIPPrefix string `json:"remote_ip_prefix" mapstructure:"remote_ip_prefix"`
 | 
			
		||||
 | 
			
		||||
	// The owner of this security group rule.
 | 
			
		||||
	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SecGroupRulePage is the page returned by a pager when traversing over a
 | 
			
		||||
// collection of security group rules.
 | 
			
		||||
type SecGroupRulePage struct {
 | 
			
		||||
	pagination.LinkedPageBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextPageURL is invoked when a paginated collection of security group rules has
 | 
			
		||||
// reached the end of a page and the pager seeks to traverse over a new one. In
 | 
			
		||||
// order to do this, it needs to construct the next page's URL.
 | 
			
		||||
func (p SecGroupRulePage) NextPageURL() (string, error) {
 | 
			
		||||
	type resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"security_group_rules_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a SecGroupRulePage struct is empty.
 | 
			
		||||
func (p SecGroupRulePage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractRules(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractRules accepts a Page struct, specifically a SecGroupRulePage struct,
 | 
			
		||||
// and extracts the elements into a slice of SecGroupRule structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractRules(page pagination.Page) ([]SecGroupRule, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		SecGroupRules []SecGroupRule `mapstructure:"security_group_rules" json:"security_group_rules"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(SecGroupRulePage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.SecGroupRules, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract is a function that accepts a result and extracts a security rule.
 | 
			
		||||
func (r commonResult) Extract() (*SecGroupRule, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		SecGroupRule *SecGroupRule `mapstructure:"security_group_rule" json:"security_group_rule"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.SecGroupRule, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateResult represents the result of a create operation.
 | 
			
		||||
type CreateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
package rules
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
const rootPath = "security-group-rules"
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL(rootPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL(rootPath, id)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,8 +0,0 @@
 | 
			
		||||
// Package ports contains functionality for working with Neutron port resources.
 | 
			
		||||
// A port represents a virtual switch port on a logical network switch. Virtual
 | 
			
		||||
// instances attach their interfaces into ports. The logical port also defines
 | 
			
		||||
// the MAC address and the IP address(es) to be assigned to the interfaces
 | 
			
		||||
// plugged into them. When IP addresses are associated to a port, this also
 | 
			
		||||
// implies the port is associated with a subnet, as the IP address was taken
 | 
			
		||||
// from the allocation pool for a specific subnet.
 | 
			
		||||
package ports
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,11 +0,0 @@
 | 
			
		||||
package ports
 | 
			
		||||
 | 
			
		||||
import "fmt"
 | 
			
		||||
 | 
			
		||||
func err(str string) error {
 | 
			
		||||
	return fmt.Errorf("%s", str)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	errNetworkIDRequired = err("A Network ID is required")
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										268
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										268
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,268 +0,0 @@
 | 
			
		||||
package ports
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AdminState gives users a solid type to work with for create and update
 | 
			
		||||
// operations. It is recommended that users use the `Up` and `Down` enums.
 | 
			
		||||
type AdminState *bool
 | 
			
		||||
 | 
			
		||||
// Convenience vars for AdminStateUp values.
 | 
			
		||||
var (
 | 
			
		||||
	iTrue  = true
 | 
			
		||||
	iFalse = false
 | 
			
		||||
 | 
			
		||||
	Up   AdminState = &iTrue
 | 
			
		||||
	Down AdminState = &iFalse
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ListOptsBuilder allows extensions to add additional parameters to the
 | 
			
		||||
// List request.
 | 
			
		||||
type ListOptsBuilder interface {
 | 
			
		||||
	ToPortListQuery() (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 port attributes you want to see returned. SortKey allows you to sort
 | 
			
		||||
// by a particular port attribute. SortDir sets the direction, and is either
 | 
			
		||||
// `asc' or `desc'. Marker and Limit are used for pagination.
 | 
			
		||||
type ListOpts struct {
 | 
			
		||||
	Status       string `q:"status"`
 | 
			
		||||
	Name         string `q:"name"`
 | 
			
		||||
	AdminStateUp *bool  `q:"admin_state_up"`
 | 
			
		||||
	NetworkID    string `q:"network_id"`
 | 
			
		||||
	TenantID     string `q:"tenant_id"`
 | 
			
		||||
	DeviceOwner  string `q:"device_owner"`
 | 
			
		||||
	MACAddress   string `q:"mac_address"`
 | 
			
		||||
	ID           string `q:"id"`
 | 
			
		||||
	DeviceID     string `q:"device_id"`
 | 
			
		||||
	Limit        int    `q:"limit"`
 | 
			
		||||
	Marker       string `q:"marker"`
 | 
			
		||||
	SortKey      string `q:"sort_key"`
 | 
			
		||||
	SortDir      string `q:"sort_dir"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToPortListQuery formats a ListOpts into a query string.
 | 
			
		||||
func (opts ListOpts) ToPortListQuery() (string, error) {
 | 
			
		||||
	q, err := gophercloud.BuildQueryString(opts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return q.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// List returns a Pager which allows you to iterate over a collection of
 | 
			
		||||
// ports. It accepts a ListOpts struct, which allows you to filter and sort
 | 
			
		||||
// the returned collection for greater efficiency.
 | 
			
		||||
//
 | 
			
		||||
// Default policy settings return only those ports that are owned by the tenant
 | 
			
		||||
// who submits the request, unless the request is submitted by a user with
 | 
			
		||||
// administrative rights.
 | 
			
		||||
func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
 | 
			
		||||
	url := listURL(c)
 | 
			
		||||
	if opts != nil {
 | 
			
		||||
		query, err := opts.ToPortListQuery()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return pagination.Pager{Err: err}
 | 
			
		||||
		}
 | 
			
		||||
		url += query
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 | 
			
		||||
		return PortPage{pagination.LinkedPageBase{PageResult: r}}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retrieves a specific port based on its unique ID.
 | 
			
		||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
 | 
			
		||||
	var res GetResult
 | 
			
		||||
	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	ToPortCreateMap() (map[string]interface{}, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateOpts represents the attributes used when creating a new port.
 | 
			
		||||
type CreateOpts struct {
 | 
			
		||||
	NetworkID           string
 | 
			
		||||
	Name                string
 | 
			
		||||
	AdminStateUp        *bool
 | 
			
		||||
	MACAddress          string
 | 
			
		||||
	FixedIPs            interface{}
 | 
			
		||||
	DeviceID            string
 | 
			
		||||
	DeviceOwner         string
 | 
			
		||||
	TenantID            string
 | 
			
		||||
	SecurityGroups      []string
 | 
			
		||||
	AllowedAddressPairs []AddressPair
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToPortCreateMap casts a CreateOpts struct to a map.
 | 
			
		||||
func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
 | 
			
		||||
	p := make(map[string]interface{})
 | 
			
		||||
 | 
			
		||||
	if opts.NetworkID == "" {
 | 
			
		||||
		return nil, errNetworkIDRequired
 | 
			
		||||
	}
 | 
			
		||||
	p["network_id"] = opts.NetworkID
 | 
			
		||||
 | 
			
		||||
	if opts.DeviceID != "" {
 | 
			
		||||
		p["device_id"] = opts.DeviceID
 | 
			
		||||
	}
 | 
			
		||||
	if opts.DeviceOwner != "" {
 | 
			
		||||
		p["device_owner"] = opts.DeviceOwner
 | 
			
		||||
	}
 | 
			
		||||
	if opts.FixedIPs != nil {
 | 
			
		||||
		p["fixed_ips"] = opts.FixedIPs
 | 
			
		||||
	}
 | 
			
		||||
	if opts.SecurityGroups != nil {
 | 
			
		||||
		p["security_groups"] = opts.SecurityGroups
 | 
			
		||||
	}
 | 
			
		||||
	if opts.TenantID != "" {
 | 
			
		||||
		p["tenant_id"] = opts.TenantID
 | 
			
		||||
	}
 | 
			
		||||
	if opts.AdminStateUp != nil {
 | 
			
		||||
		p["admin_state_up"] = &opts.AdminStateUp
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Name != "" {
 | 
			
		||||
		p["name"] = opts.Name
 | 
			
		||||
	}
 | 
			
		||||
	if opts.MACAddress != "" {
 | 
			
		||||
		p["mac_address"] = opts.MACAddress
 | 
			
		||||
	}
 | 
			
		||||
	if opts.AllowedAddressPairs != nil {
 | 
			
		||||
		p["allowed_address_pairs"] = opts.AllowedAddressPairs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return map[string]interface{}{"port": p}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create accepts a CreateOpts struct and creates a new network using the values
 | 
			
		||||
// provided. You must remember to provide a NetworkID value.
 | 
			
		||||
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
 | 
			
		||||
	var res CreateResult
 | 
			
		||||
 | 
			
		||||
	reqBody, err := opts.ToPortCreateMap()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		res.Err = err
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	ToPortUpdateMap() (map[string]interface{}, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateOpts represents the attributes used when updating an existing port.
 | 
			
		||||
type UpdateOpts struct {
 | 
			
		||||
	Name                string
 | 
			
		||||
	AdminStateUp        *bool
 | 
			
		||||
	FixedIPs            interface{}
 | 
			
		||||
	DeviceID            string
 | 
			
		||||
	DeviceOwner         string
 | 
			
		||||
	SecurityGroups      []string
 | 
			
		||||
	AllowedAddressPairs []AddressPair
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToPortUpdateMap casts an UpdateOpts struct to a map.
 | 
			
		||||
func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) {
 | 
			
		||||
	p := make(map[string]interface{})
 | 
			
		||||
 | 
			
		||||
	if opts.DeviceID != "" {
 | 
			
		||||
		p["device_id"] = opts.DeviceID
 | 
			
		||||
	}
 | 
			
		||||
	if opts.DeviceOwner != "" {
 | 
			
		||||
		p["device_owner"] = opts.DeviceOwner
 | 
			
		||||
	}
 | 
			
		||||
	if opts.FixedIPs != nil {
 | 
			
		||||
		p["fixed_ips"] = opts.FixedIPs
 | 
			
		||||
	}
 | 
			
		||||
	if opts.SecurityGroups != nil {
 | 
			
		||||
		p["security_groups"] = opts.SecurityGroups
 | 
			
		||||
	}
 | 
			
		||||
	if opts.AdminStateUp != nil {
 | 
			
		||||
		p["admin_state_up"] = &opts.AdminStateUp
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Name != "" {
 | 
			
		||||
		p["name"] = opts.Name
 | 
			
		||||
	}
 | 
			
		||||
	if opts.AllowedAddressPairs != nil {
 | 
			
		||||
		p["allowed_address_pairs"] = opts.AllowedAddressPairs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return map[string]interface{}{"port": p}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update accepts a UpdateOpts struct and updates an existing port using the
 | 
			
		||||
// values provided.
 | 
			
		||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
 | 
			
		||||
	var res UpdateResult
 | 
			
		||||
 | 
			
		||||
	reqBody, err := opts.ToPortUpdateMap()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		res.Err = err
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, res.Err = c.Put(updateURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
 | 
			
		||||
		OkCodes: []int{200, 201},
 | 
			
		||||
	})
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete accepts a unique ID and deletes the port associated with it.
 | 
			
		||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 | 
			
		||||
	var res DeleteResult
 | 
			
		||||
	_, res.Err = c.Delete(deleteURL(c, id), nil)
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IDFromName is a convenience function that returns a port's ID given its name.
 | 
			
		||||
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
 | 
			
		||||
	portCount := 0
 | 
			
		||||
	portID := ""
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return "", fmt.Errorf("A port name must be provided.")
 | 
			
		||||
	}
 | 
			
		||||
	pager := List(client, nil)
 | 
			
		||||
	pager.EachPage(func(page pagination.Page) (bool, error) {
 | 
			
		||||
		portList, err := ExtractPorts(page)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, p := range portList {
 | 
			
		||||
			if p.Name == name {
 | 
			
		||||
				portCount++
 | 
			
		||||
				portID = p.ID
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return true, nil
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	switch portCount {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return "", fmt.Errorf("Unable to find port: %s", name)
 | 
			
		||||
	case 1:
 | 
			
		||||
		return portID, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return "", fmt.Errorf("Found %d ports matching %s", portCount, name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										132
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/results.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										132
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/results.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,132 +0,0 @@
 | 
			
		||||
package ports
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"github.com/rackspace/gophercloud"
 | 
			
		||||
	"github.com/rackspace/gophercloud/pagination"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type commonResult struct {
 | 
			
		||||
	gophercloud.Result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract is a function that accepts a result and extracts a port resource.
 | 
			
		||||
func (r commonResult) Extract() (*Port, error) {
 | 
			
		||||
	if r.Err != nil {
 | 
			
		||||
		return nil, r.Err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res struct {
 | 
			
		||||
		Port *Port `json:"port"`
 | 
			
		||||
	}
 | 
			
		||||
	err := mapstructure.Decode(r.Body, &res)
 | 
			
		||||
 | 
			
		||||
	return res.Port, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateResult represents the result of a create operation.
 | 
			
		||||
type CreateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetResult represents the result of a get operation.
 | 
			
		||||
type GetResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateResult represents the result of an update operation.
 | 
			
		||||
type UpdateResult struct {
 | 
			
		||||
	commonResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteResult represents the result of a delete operation.
 | 
			
		||||
type DeleteResult struct {
 | 
			
		||||
	gophercloud.ErrResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IP is a sub-struct that represents an individual IP.
 | 
			
		||||
type IP struct {
 | 
			
		||||
	SubnetID  string `mapstructure:"subnet_id" json:"subnet_id"`
 | 
			
		||||
	IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AddressPair struct {
 | 
			
		||||
	IPAddress  string `mapstructure:"ip_address" json:"ip_address,omitempty"`
 | 
			
		||||
	MACAddress string `mapstructure:"mac_address" json:"mac_address,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Port represents a Neutron port. See package documentation for a top-level
 | 
			
		||||
// description of what this is.
 | 
			
		||||
type Port struct {
 | 
			
		||||
	// UUID for the port.
 | 
			
		||||
	ID string `mapstructure:"id" json:"id"`
 | 
			
		||||
	// Network that this port is associated with.
 | 
			
		||||
	NetworkID string `mapstructure:"network_id" json:"network_id"`
 | 
			
		||||
	// Human-readable name for the port. Might not be unique.
 | 
			
		||||
	Name string `mapstructure:"name" json:"name"`
 | 
			
		||||
	// Administrative state of port. If false (down), port does not forward packets.
 | 
			
		||||
	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
 | 
			
		||||
	// Indicates whether network is currently operational. Possible values include
 | 
			
		||||
	// `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values.
 | 
			
		||||
	Status string `mapstructure:"status" json:"status"`
 | 
			
		||||
	// Mac address to use on this port.
 | 
			
		||||
	MACAddress string `mapstructure:"mac_address" json:"mac_address"`
 | 
			
		||||
	// Specifies IP addresses for the port thus associating the port itself with
 | 
			
		||||
	// the subnets where the IP addresses are picked from
 | 
			
		||||
	FixedIPs []IP `mapstructure:"fixed_ips" json:"fixed_ips"`
 | 
			
		||||
	// Owner of network. Only admin users can specify a tenant_id other than its own.
 | 
			
		||||
	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
 | 
			
		||||
	// Identifies the entity (e.g.: dhcp agent) using this port.
 | 
			
		||||
	DeviceOwner string `mapstructure:"device_owner" json:"device_owner"`
 | 
			
		||||
	// Specifies the IDs of any security groups associated with a port.
 | 
			
		||||
	SecurityGroups []string `mapstructure:"security_groups" json:"security_groups"`
 | 
			
		||||
	// Identifies the device (e.g., virtual server) using this port.
 | 
			
		||||
	DeviceID string `mapstructure:"device_id" json:"device_id"`
 | 
			
		||||
	// Identifies the list of IP addresses the port will recognize/accept
 | 
			
		||||
	AllowedAddressPairs []AddressPair `mapstructure:"allowed_address_pairs" json:"allowed_address_pairs"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PortPage is the page returned by a pager when traversing over a collection
 | 
			
		||||
// of network ports.
 | 
			
		||||
type PortPage struct {
 | 
			
		||||
	pagination.LinkedPageBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextPageURL is invoked when a paginated collection of ports has reached
 | 
			
		||||
// the end of a page and the pager seeks to traverse over a new one. In order
 | 
			
		||||
// to do this, it needs to construct the next page's URL.
 | 
			
		||||
func (p PortPage) NextPageURL() (string, error) {
 | 
			
		||||
	type resp struct {
 | 
			
		||||
		Links []gophercloud.Link `mapstructure:"ports_links"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r resp
 | 
			
		||||
	err := mapstructure.Decode(p.Body, &r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return gophercloud.ExtractNextURL(r.Links)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks whether a PortPage struct is empty.
 | 
			
		||||
func (p PortPage) IsEmpty() (bool, error) {
 | 
			
		||||
	is, err := ExtractPorts(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return len(is) == 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractPorts accepts a Page struct, specifically a PortPage struct,
 | 
			
		||||
// and extracts the elements into a slice of Port structs. In other words,
 | 
			
		||||
// a generic collection is mapped into a relevant slice.
 | 
			
		||||
func ExtractPorts(page pagination.Page) ([]Port, error) {
 | 
			
		||||
	var resp struct {
 | 
			
		||||
		Ports []Port `mapstructure:"ports" json:"ports"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := mapstructure.Decode(page.(PortPage).Body, &resp)
 | 
			
		||||
 | 
			
		||||
	return resp.Ports, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/urls.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/urls.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,31 +0,0 @@
 | 
			
		||||
package ports
 | 
			
		||||
 | 
			
		||||
import "github.com/rackspace/gophercloud"
 | 
			
		||||
 | 
			
		||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return c.ServiceURL("ports", id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func rootURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return c.ServiceURL("ports")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func listURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return rootURL(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return resourceURL(c, id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createURL(c *gophercloud.ServiceClient) string {
 | 
			
		||||
	return rootURL(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return resourceURL(c, id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deleteURL(c *gophercloud.ServiceClient, id string) string {
 | 
			
		||||
	return resourceURL(c, id)
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user