Move to vendor
As we walk away from 1.4, godep no longer requires to have stuff into workspace and we no longer need the symlink. Signed-off-by: Olivier Gambier <olivier@docker.com>
This commit is contained in:
4
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/doc.go
generated
vendored
Normal file
4
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/doc.go
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
// Package apiversions provides information and interaction with the different
|
||||
// API versions for the OpenStack Neutron service. This functionality is not
|
||||
// restricted to this particular version.
|
||||
package apiversions
|
||||
1
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/errors.go
generated
vendored
Normal file
1
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/errors.go
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package apiversions
|
||||
21
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/requests.go
generated
vendored
Normal file
21
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/requests.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package apiversions
|
||||
|
||||
import (
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ListVersions lists all the Neutron API versions available to end-users
|
||||
func ListVersions(c *gophercloud.ServiceClient) pagination.Pager {
|
||||
return pagination.NewPager(c, apiVersionsURL(c), func(r pagination.PageResult) pagination.Page {
|
||||
return APIVersionPage{pagination.SinglePageBase(r)}
|
||||
})
|
||||
}
|
||||
|
||||
// ListVersionResources lists all of the different API resources for a particular
|
||||
// API versions. Typical resources for Neutron might be: networks, subnets, etc.
|
||||
func ListVersionResources(c *gophercloud.ServiceClient, v string) pagination.Pager {
|
||||
return pagination.NewPager(c, apiInfoURL(c, v), func(r pagination.PageResult) pagination.Page {
|
||||
return APIVersionResourcePage{pagination.SinglePageBase(r)}
|
||||
})
|
||||
}
|
||||
182
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/requests_test.go
generated
vendored
Normal file
182
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
package apiversions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
fake "github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
func TestListVersions(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"versions": [
|
||||
{
|
||||
"status": "CURRENT",
|
||||
"id": "v2.0",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://23.253.228.211:9696/v2.0",
|
||||
"rel": "self"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractAPIVersions(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract API versions: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []APIVersion{
|
||||
APIVersion{
|
||||
Status: "CURRENT",
|
||||
ID: "v2.0",
|
||||
},
|
||||
}
|
||||
|
||||
th.AssertDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonJSONCannotBeExtractedIntoAPIVersions(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
|
||||
if _, err := ExtractAPIVersions(page); err == nil {
|
||||
t.Fatalf("Expected error, got nil")
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPIInfo(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"resources": [
|
||||
{
|
||||
"links": [
|
||||
{
|
||||
"href": "http://23.253.228.211:9696/v2.0/subnets",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"name": "subnet",
|
||||
"collection": "subnets"
|
||||
},
|
||||
{
|
||||
"links": [
|
||||
{
|
||||
"href": "http://23.253.228.211:9696/v2.0/networks",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"name": "network",
|
||||
"collection": "networks"
|
||||
},
|
||||
{
|
||||
"links": [
|
||||
{
|
||||
"href": "http://23.253.228.211:9696/v2.0/ports",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"name": "port",
|
||||
"collection": "ports"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractVersionResources(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract version resources: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []APIVersionResource{
|
||||
APIVersionResource{
|
||||
Name: "subnet",
|
||||
Collection: "subnets",
|
||||
},
|
||||
APIVersionResource{
|
||||
Name: "network",
|
||||
Collection: "networks",
|
||||
},
|
||||
APIVersionResource{
|
||||
Name: "port",
|
||||
Collection: "ports",
|
||||
},
|
||||
}
|
||||
|
||||
th.AssertDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonJSONCannotBeExtractedIntoAPIVersionResources(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(func(page pagination.Page) (bool, error) {
|
||||
if _, err := ExtractVersionResources(page); err == nil {
|
||||
t.Fatalf("Expected error, got nil")
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
77
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/results.go
generated
vendored
Normal file
77
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/results.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
package apiversions
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// APIVersion represents an API version for Neutron. It contains the status of
|
||||
// the API, and its unique ID.
|
||||
type APIVersion struct {
|
||||
Status string `mapstructure:"status" json:"status"`
|
||||
ID string `mapstructure:"id" json:"id"`
|
||||
}
|
||||
|
||||
// APIVersionPage is the page returned by a pager when traversing over a
|
||||
// collection of API versions.
|
||||
type APIVersionPage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty checks whether an APIVersionPage struct is empty.
|
||||
func (r APIVersionPage) IsEmpty() (bool, error) {
|
||||
is, err := ExtractAPIVersions(r)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return len(is) == 0, nil
|
||||
}
|
||||
|
||||
// ExtractAPIVersions takes a collection page, extracts all of the elements,
|
||||
// and returns them a slice of APIVersion structs. It is effectively a cast.
|
||||
func ExtractAPIVersions(page pagination.Page) ([]APIVersion, error) {
|
||||
var resp struct {
|
||||
Versions []APIVersion `mapstructure:"versions"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(page.(APIVersionPage).Body, &resp)
|
||||
|
||||
return resp.Versions, err
|
||||
}
|
||||
|
||||
// APIVersionResource represents a generic API resource. It contains the name
|
||||
// of the resource and its plural collection name.
|
||||
type APIVersionResource struct {
|
||||
Name string `mapstructure:"name" json:"name"`
|
||||
Collection string `mapstructure:"collection" json:"collection"`
|
||||
}
|
||||
|
||||
// APIVersionResourcePage is a concrete type which embeds the common
|
||||
// SinglePageBase struct, and is used when traversing API versions collections.
|
||||
type APIVersionResourcePage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty is a concrete function which indicates whether an
|
||||
// APIVersionResourcePage is empty or not.
|
||||
func (r APIVersionResourcePage) IsEmpty() (bool, error) {
|
||||
is, err := ExtractVersionResources(r)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return len(is) == 0, nil
|
||||
}
|
||||
|
||||
// ExtractVersionResources accepts a Page struct, specifically a
|
||||
// APIVersionResourcePage struct, and extracts the elements into a slice of
|
||||
// APIVersionResource structs. In other words, the collection is mapped into
|
||||
// a relevant slice.
|
||||
func ExtractVersionResources(page pagination.Page) ([]APIVersionResource, error) {
|
||||
var resp struct {
|
||||
APIVersionResources []APIVersionResource `mapstructure:"resources"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(page.(APIVersionResourcePage).Body, &resp)
|
||||
|
||||
return resp.APIVersionResources, err
|
||||
}
|
||||
15
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/urls.go
generated
vendored
Normal file
15
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/urls.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package apiversions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
)
|
||||
|
||||
func apiVersionsURL(c *gophercloud.ServiceClient) string {
|
||||
return c.Endpoint
|
||||
}
|
||||
|
||||
func apiInfoURL(c *gophercloud.ServiceClient, version string) string {
|
||||
return c.Endpoint + strings.TrimRight(version, "/") + "/"
|
||||
}
|
||||
26
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/urls_test.go
generated
vendored
Normal file
26
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/apiversions/urls_test.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package apiversions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
const endpoint = "http://localhost:57909/"
|
||||
|
||||
func endpointClient() *gophercloud.ServiceClient {
|
||||
return &gophercloud.ServiceClient{Endpoint: endpoint}
|
||||
}
|
||||
|
||||
func TestAPIVersionsURL(t *testing.T) {
|
||||
actual := apiVersionsURL(endpointClient())
|
||||
expected := endpoint
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestAPIInfoURL(t *testing.T) {
|
||||
actual := apiInfoURL(endpointClient(), "v2.0")
|
||||
expected := endpoint + "v2.0/"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
14
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/common/common_tests.go
generated
vendored
Normal file
14
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/common/common_tests.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
const TokenID = client.TokenID
|
||||
|
||||
func ServiceClient() *gophercloud.ServiceClient {
|
||||
sc := client.ServiceClient()
|
||||
sc.ResourceBase = sc.Endpoint + "v2.0/"
|
||||
return sc
|
||||
}
|
||||
41
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/delegate.go
generated
vendored
Normal file
41
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/delegate.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
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)
|
||||
}
|
||||
105
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/delegate_test.go
generated
vendored
Normal file
105
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/delegate_test.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
package extensions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
common "github.com/rackspace/gophercloud/openstack/common/extensions"
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/extensions", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractExtensions(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract extensions: %v", err)
|
||||
}
|
||||
|
||||
expected := []Extension{
|
||||
Extension{
|
||||
common.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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
th.AssertDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/extensions/agent", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"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."
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
ext, err := Get(fake.ServiceClient(), "agent").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, ext.Updated, "2013-02-03T10:00:00-00:00")
|
||||
th.AssertEquals(t, ext.Name, "agent")
|
||||
th.AssertEquals(t, ext.Namespace, "http://docs.openstack.org/ext/agent/api/v2.0")
|
||||
th.AssertEquals(t, ext.Alias, "agent")
|
||||
th.AssertEquals(t, ext.Description, "The agent management extension.")
|
||||
}
|
||||
3
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/doc.go
generated
vendored
Normal file
3
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package external provides information and interaction with the external
|
||||
// extension for the OpenStack Networking service.
|
||||
package external
|
||||
69
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/requests.go
generated
vendored
Normal file
69
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/requests.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package external
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
)
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
// CreateOpts is the structure used when creating new external network
|
||||
// resources. It embeds networks.CreateOpts and so inherits all of its required
|
||||
// and optional fields, with the addition of the External field.
|
||||
type CreateOpts struct {
|
||||
Parent networks.CreateOpts
|
||||
External bool
|
||||
}
|
||||
|
||||
// ToNetworkCreateMap casts a CreateOpts struct to a map.
|
||||
func (o CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
|
||||
|
||||
// DO NOT REMOVE. Though this line seemingly does nothing of value, it is a
|
||||
// splint to prevent the unit test from failing on Go Tip. We suspect it is a
|
||||
// compiler issue that will hopefully be worked out prior to our next release.
|
||||
// Again, for all the unit tests to pass, this line is necessary and sufficient
|
||||
// at the moment. We should reassess after the Go 1.5 release to determine
|
||||
// if this line is still needed.
|
||||
time.Sleep(0 * time.Millisecond)
|
||||
|
||||
outer, err := o.Parent.ToNetworkCreateMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outer["network"].(map[string]interface{})["router:external"] = o.External
|
||||
|
||||
return outer, nil
|
||||
}
|
||||
|
||||
// UpdateOpts is the structure used when updating existing external network
|
||||
// resources. It embeds networks.UpdateOpts and so inherits all of its required
|
||||
// and optional fields, with the addition of the External field.
|
||||
type UpdateOpts struct {
|
||||
Parent networks.UpdateOpts
|
||||
External bool
|
||||
}
|
||||
|
||||
// ToNetworkUpdateMap casts an UpdateOpts struct to a map.
|
||||
func (o UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) {
|
||||
outer, err := o.Parent.ToNetworkUpdateMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outer["network"].(map[string]interface{})["router:external"] = o.External
|
||||
|
||||
return outer, nil
|
||||
}
|
||||
81
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/results.go
generated
vendored
Normal file
81
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/results.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package external
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// NetworkExternal represents a decorated form of a Network with based on the
|
||||
// "external-net" extension.
|
||||
type NetworkExternal struct {
|
||||
// UUID for the network
|
||||
ID string `mapstructure:"id" json:"id"`
|
||||
|
||||
// Human-readable name for the network. Might not be unique.
|
||||
Name string `mapstructure:"name" json:"name"`
|
||||
|
||||
// The administrative state of network. If false (down), the network 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"`
|
||||
|
||||
// Subnets associated with this network.
|
||||
Subnets []string `mapstructure:"subnets" json:"subnets"`
|
||||
|
||||
// Owner of network. Only admin users can specify a tenant_id other than its own.
|
||||
TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
|
||||
|
||||
// Specifies whether the network resource can be accessed by any tenant or not.
|
||||
Shared bool `mapstructure:"shared" json:"shared"`
|
||||
|
||||
// Specifies whether the network is an external network or not.
|
||||
External bool `mapstructure:"router:external" json:"router:external"`
|
||||
}
|
||||
|
||||
func commonExtract(e error, response interface{}) (*NetworkExternal, error) {
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Network *NetworkExternal `json:"network"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(response, &res)
|
||||
|
||||
return res.Network, err
|
||||
}
|
||||
|
||||
// ExtractGet decorates a GetResult struct returned from a networks.Get()
|
||||
// function with extended attributes.
|
||||
func ExtractGet(r networks.GetResult) (*NetworkExternal, error) {
|
||||
return commonExtract(r.Err, r.Body)
|
||||
}
|
||||
|
||||
// ExtractCreate decorates a CreateResult struct returned from a networks.Create()
|
||||
// function with extended attributes.
|
||||
func ExtractCreate(r networks.CreateResult) (*NetworkExternal, error) {
|
||||
return commonExtract(r.Err, r.Body)
|
||||
}
|
||||
|
||||
// ExtractUpdate decorates a UpdateResult struct returned from a
|
||||
// networks.Update() function with extended attributes.
|
||||
func ExtractUpdate(r networks.UpdateResult) (*NetworkExternal, error) {
|
||||
return commonExtract(r.Err, r.Body)
|
||||
}
|
||||
|
||||
// ExtractList accepts a Page struct, specifically a NetworkPage struct, and
|
||||
// extracts the elements into a slice of NetworkExternal structs. In other
|
||||
// words, a generic collection is mapped into a relevant slice.
|
||||
func ExtractList(page pagination.Page) ([]NetworkExternal, error) {
|
||||
var resp struct {
|
||||
Networks []NetworkExternal `mapstructure:"networks" json:"networks"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(page.(networks.NetworkPage).Body, &resp)
|
||||
|
||||
return resp.Networks, err
|
||||
}
|
||||
254
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/results_test.go
generated
vendored
Normal file
254
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/results_test.go
generated
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
package external
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
fake "github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"networks": [
|
||||
{
|
||||
"admin_state_up": true,
|
||||
"id": "0f38d5ad-10a6-428f-a5fc-825cfe0f1970",
|
||||
"name": "net1",
|
||||
"router:external": false,
|
||||
"shared": false,
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"25778974-48a8-46e7-8998-9dc8c70d2f06"
|
||||
],
|
||||
"tenant_id": "b575417a6c444a6eb5cc3a58eb4f714a"
|
||||
},
|
||||
{
|
||||
"admin_state_up": true,
|
||||
"id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
|
||||
"name": "ext_net",
|
||||
"router:external": true,
|
||||
"shared": false,
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
|
||||
],
|
||||
"tenant_id": "5eb8995cf717462c9df8d1edfa498010"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
networks.List(fake.ServiceClient(), networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractList(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract networks: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []NetworkExternal{
|
||||
NetworkExternal{
|
||||
Status: "ACTIVE",
|
||||
Subnets: []string{"25778974-48a8-46e7-8998-9dc8c70d2f06"},
|
||||
Name: "net1",
|
||||
AdminStateUp: true,
|
||||
TenantID: "b575417a6c444a6eb5cc3a58eb4f714a",
|
||||
Shared: false,
|
||||
ID: "0f38d5ad-10a6-428f-a5fc-825cfe0f1970",
|
||||
External: false,
|
||||
},
|
||||
NetworkExternal{
|
||||
Status: "ACTIVE",
|
||||
Subnets: []string{"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"},
|
||||
Name: "ext_net",
|
||||
AdminStateUp: true,
|
||||
TenantID: "5eb8995cf717462c9df8d1edfa498010",
|
||||
Shared: false,
|
||||
ID: "8d05a1b1-297a-46ca-8974-17debf51ca3c",
|
||||
External: true,
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"network": {
|
||||
"admin_state_up": true,
|
||||
"id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
|
||||
"name": "ext_net",
|
||||
"router:external": true,
|
||||
"shared": false,
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
|
||||
],
|
||||
"tenant_id": "5eb8995cf717462c9df8d1edfa498010"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
res := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
|
||||
n, err := ExtractGet(res)
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, true, n.External)
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"network": {
|
||||
"admin_state_up": true,
|
||||
"name": "ext_net",
|
||||
"router:external": true
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"network": {
|
||||
"admin_state_up": true,
|
||||
"id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
|
||||
"name": "ext_net",
|
||||
"router:external": true,
|
||||
"shared": false,
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
|
||||
],
|
||||
"tenant_id": "5eb8995cf717462c9df8d1edfa498010"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := CreateOpts{networks.CreateOpts{Name: "ext_net", AdminStateUp: Up}, true}
|
||||
res := networks.Create(fake.ServiceClient(), options)
|
||||
|
||||
n, err := ExtractCreate(res)
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, true, n.External)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"network": {
|
||||
"router:external": true,
|
||||
"name": "new_name"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"network": {
|
||||
"admin_state_up": true,
|
||||
"id": "8d05a1b1-297a-46ca-8974-17debf51ca3c",
|
||||
"name": "new_name",
|
||||
"router:external": true,
|
||||
"shared": false,
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"
|
||||
],
|
||||
"tenant_id": "5eb8995cf717462c9df8d1edfa498010"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := UpdateOpts{networks.UpdateOpts{Name: "new_name"}, true}
|
||||
res := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options)
|
||||
n, err := ExtractUpdate(res)
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, true, n.External)
|
||||
}
|
||||
|
||||
func TestExtractFnsReturnsErrWhenResultContainsErr(t *testing.T) {
|
||||
gr := networks.GetResult{}
|
||||
gr.Err = errors.New("")
|
||||
|
||||
if _, err := ExtractGet(gr); err == nil {
|
||||
t.Fatalf("Expected error, got one")
|
||||
}
|
||||
|
||||
ur := networks.UpdateResult{}
|
||||
ur.Err = errors.New("")
|
||||
|
||||
if _, err := ExtractUpdate(ur); err == nil {
|
||||
t.Fatalf("Expected error, got one")
|
||||
}
|
||||
|
||||
cr := networks.CreateResult{}
|
||||
cr.Err = errors.New("")
|
||||
|
||||
if _, err := ExtractCreate(cr); err == nil {
|
||||
t.Fatalf("Expected error, got one")
|
||||
}
|
||||
}
|
||||
3
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/doc.go
generated
vendored
Normal file
3
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package fwaas provides information and interaction with the Firewall
|
||||
// as a Service extension for the OpenStack Networking service.
|
||||
package fwaas
|
||||
11
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/errors.go
generated
vendored
Normal file
11
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/errors.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package firewalls
|
||||
|
||||
import "fmt"
|
||||
|
||||
func err(str string) error {
|
||||
return fmt.Errorf("%s", str)
|
||||
}
|
||||
|
||||
var (
|
||||
errPolicyRequired = err("A policy ID is required")
|
||||
)
|
||||
216
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/requests.go
generated
vendored
Normal file
216
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/requests.go
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
package firewalls
|
||||
|
||||
import (
|
||||
"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
|
||||
|
||||
// Shared gives users a solid type to work with for create and update
|
||||
// operations. It is recommended that users use the `Yes` and `No` enums.
|
||||
type Shared *bool
|
||||
|
||||
// Convenience vars for AdminStateUp and Shared values.
|
||||
var (
|
||||
iTrue = true
|
||||
iFalse = false
|
||||
Up AdminState = &iTrue
|
||||
Down AdminState = &iFalse
|
||||
Yes Shared = &iTrue
|
||||
No Shared = &iFalse
|
||||
)
|
||||
|
||||
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||
// List request.
|
||||
type ListOptsBuilder interface {
|
||||
ToFirewallListQuery() (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 firewall attributes you want to see returned. SortKey allows you to sort
|
||||
// by a particular firewall attribute. SortDir sets the direction, and is either
|
||||
// `asc' or `desc'. Marker and Limit are used for pagination.
|
||||
type ListOpts struct {
|
||||
TenantID string `q:"tenant_id"`
|
||||
Name string `q:"name"`
|
||||
Description string `q:"description"`
|
||||
AdminStateUp bool `q:"admin_state_up"`
|
||||
Shared bool `q:"shared"`
|
||||
PolicyID string `q:"firewall_policy_id"`
|
||||
ID string `q:"id"`
|
||||
Limit int `q:"limit"`
|
||||
Marker string `q:"marker"`
|
||||
SortKey string `q:"sort_key"`
|
||||
SortDir string `q:"sort_dir"`
|
||||
}
|
||||
|
||||
// ToFirewallListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToFirewallListQuery() (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
|
||||
// firewalls. It accepts a ListOpts struct, which allows you to filter
|
||||
// and sort the returned collection for greater efficiency.
|
||||
//
|
||||
// Default policy settings return only those firewalls 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.ToFirewallListQuery()
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
url += query
|
||||
}
|
||||
|
||||
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
|
||||
return FirewallPage{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 {
|
||||
ToFirewallCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts contains all the values needed to create a new firewall.
|
||||
type CreateOpts struct {
|
||||
// Only required if the caller has an admin role and wants to create a firewall
|
||||
// for another tenant.
|
||||
TenantID string
|
||||
Name string
|
||||
Description string
|
||||
AdminStateUp *bool
|
||||
Shared *bool
|
||||
PolicyID string
|
||||
}
|
||||
|
||||
// ToFirewallCreateMap casts a CreateOpts struct to a map.
|
||||
func (opts CreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) {
|
||||
if opts.PolicyID == "" {
|
||||
return nil, errPolicyRequired
|
||||
}
|
||||
|
||||
f := make(map[string]interface{})
|
||||
|
||||
if opts.TenantID != "" {
|
||||
f["tenant_id"] = opts.TenantID
|
||||
}
|
||||
if opts.Name != "" {
|
||||
f["name"] = opts.Name
|
||||
}
|
||||
if opts.Description != "" {
|
||||
f["description"] = opts.Description
|
||||
}
|
||||
if opts.Shared != nil {
|
||||
f["shared"] = *opts.Shared
|
||||
}
|
||||
if opts.AdminStateUp != nil {
|
||||
f["admin_state_up"] = *opts.AdminStateUp
|
||||
}
|
||||
if opts.PolicyID != "" {
|
||||
f["firewall_policy_id"] = opts.PolicyID
|
||||
}
|
||||
|
||||
return map[string]interface{}{"firewall": f}, nil
|
||||
}
|
||||
|
||||
// Create accepts a CreateOpts struct and uses the values to create a new firewall
|
||||
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||
var res CreateResult
|
||||
|
||||
reqBody, err := opts.ToFirewallCreateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// Get retrieves a particular firewall 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 {
|
||||
ToFirewallUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateOpts contains the values used when updating a firewall.
|
||||
type UpdateOpts struct {
|
||||
// Name of the firewall.
|
||||
Name string
|
||||
Description string
|
||||
AdminStateUp *bool
|
||||
Shared *bool
|
||||
PolicyID string
|
||||
}
|
||||
|
||||
// ToFirewallUpdateMap casts a CreateOpts struct to a map.
|
||||
func (opts UpdateOpts) ToFirewallUpdateMap() (map[string]interface{}, error) {
|
||||
f := make(map[string]interface{})
|
||||
|
||||
if opts.Name != "" {
|
||||
f["name"] = opts.Name
|
||||
}
|
||||
if opts.Description != "" {
|
||||
f["description"] = opts.Description
|
||||
}
|
||||
if opts.Shared != nil {
|
||||
f["shared"] = *opts.Shared
|
||||
}
|
||||
if opts.AdminStateUp != nil {
|
||||
f["admin_state_up"] = *opts.AdminStateUp
|
||||
}
|
||||
if opts.PolicyID != "" {
|
||||
f["firewall_policy_id"] = opts.PolicyID
|
||||
}
|
||||
|
||||
return map[string]interface{}{"firewall": f}, nil
|
||||
}
|
||||
|
||||
// Update allows firewalls to be updated.
|
||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
|
||||
var res UpdateResult
|
||||
|
||||
reqBody, err := opts.ToFirewallUpdateMap()
|
||||
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 firewall 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
|
||||
}
|
||||
246
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/requests_test.go
generated
vendored
Normal file
246
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
package firewalls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestURLs(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/fw/firewalls", rootURL(fake.ServiceClient()))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewalls":[
|
||||
{
|
||||
"status": "ACTIVE",
|
||||
"name": "fw1",
|
||||
"admin_state_up": false,
|
||||
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
|
||||
"firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
|
||||
"id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
|
||||
"description": "OpenStack firewall 1"
|
||||
},
|
||||
{
|
||||
"status": "PENDING_UPDATE",
|
||||
"name": "fw2",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
|
||||
"firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e299",
|
||||
"id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f99",
|
||||
"description": "OpenStack firewall 2"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractFirewalls(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract members: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []Firewall{
|
||||
Firewall{
|
||||
Status: "ACTIVE",
|
||||
Name: "fw1",
|
||||
AdminStateUp: false,
|
||||
TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b",
|
||||
PolicyID: "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
|
||||
ID: "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
|
||||
Description: "OpenStack firewall 1",
|
||||
},
|
||||
Firewall{
|
||||
Status: "PENDING_UPDATE",
|
||||
Name: "fw2",
|
||||
AdminStateUp: true,
|
||||
TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b",
|
||||
PolicyID: "34be8c83-4d42-4dca-a74e-b77fffb8e299",
|
||||
ID: "fb5b5315-64f6-4ea3-8e58-981cc37c6f99",
|
||||
Description: "OpenStack firewall 2",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"firewall":{
|
||||
"name": "fw",
|
||||
"description": "OpenStack firewall",
|
||||
"admin_state_up": true,
|
||||
"firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
|
||||
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewall":{
|
||||
"status": "PENDING_CREATE",
|
||||
"name": "fw",
|
||||
"description": "OpenStack firewall",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
|
||||
"firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := CreateOpts{
|
||||
TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b",
|
||||
Name: "fw",
|
||||
Description: "OpenStack firewall",
|
||||
AdminStateUp: Up,
|
||||
PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
|
||||
}
|
||||
_, err := Create(fake.ServiceClient(), options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewalls/fb5b5315-64f6-4ea3-8e58-981cc37c6f61", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewall": {
|
||||
"status": "ACTIVE",
|
||||
"name": "fw",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
|
||||
"firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
|
||||
"id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
|
||||
"description": "OpenStack firewall"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
fw, err := Get(fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "ACTIVE", fw.Status)
|
||||
th.AssertEquals(t, "fw", fw.Name)
|
||||
th.AssertEquals(t, "OpenStack firewall", fw.Description)
|
||||
th.AssertEquals(t, true, fw.AdminStateUp)
|
||||
th.AssertEquals(t, "34be8c83-4d42-4dca-a74e-b77fffb8e28a", fw.PolicyID)
|
||||
th.AssertEquals(t, "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", fw.ID)
|
||||
th.AssertEquals(t, "b4eedccc6fb74fa8a7ad6b08382b852b", fw.TenantID)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewalls/ea5b5315-64f6-4ea3-8e58-981cc37c6576", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"firewall":{
|
||||
"name": "fw",
|
||||
"description": "updated fw",
|
||||
"admin_state_up":false,
|
||||
"firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewall": {
|
||||
"status": "ACTIVE",
|
||||
"name": "fw",
|
||||
"admin_state_up": false,
|
||||
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
|
||||
"firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
|
||||
"id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576",
|
||||
"description": "OpenStack firewall",
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := UpdateOpts{
|
||||
Name: "fw",
|
||||
Description: "updated fw",
|
||||
AdminStateUp: Down,
|
||||
PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
|
||||
}
|
||||
|
||||
_, err := Update(fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewalls/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
101
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/results.go
generated
vendored
Normal file
101
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/results.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
package firewalls
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
type Firewall struct {
|
||||
ID string `json:"id" mapstructure:"id"`
|
||||
Name string `json:"name" mapstructure:"name"`
|
||||
Description string `json:"description" mapstructure:"description"`
|
||||
AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
|
||||
Status string `json:"status" mapstructure:"status"`
|
||||
PolicyID string `json:"firewall_policy_id" mapstructure:"firewall_policy_id"`
|
||||
TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
|
||||
}
|
||||
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a function that accepts a result and extracts a firewall.
|
||||
func (r commonResult) Extract() (*Firewall, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Firewall *Firewall `json:"firewall"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &res)
|
||||
|
||||
return res.Firewall, err
|
||||
}
|
||||
|
||||
// FirewallPage is the page returned by a pager when traversing over a
|
||||
// collection of firewalls.
|
||||
type FirewallPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// NextPageURL is invoked when a paginated collection of firewalls 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 FirewallPage) NextPageURL() (string, error) {
|
||||
type resp struct {
|
||||
Links []gophercloud.Link `mapstructure:"firewalls_links"`
|
||||
}
|
||||
|
||||
var r resp
|
||||
err := mapstructure.Decode(p.Body, &r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return gophercloud.ExtractNextURL(r.Links)
|
||||
}
|
||||
|
||||
// IsEmpty checks whether a FirewallPage struct is empty.
|
||||
func (p FirewallPage) IsEmpty() (bool, error) {
|
||||
is, err := ExtractFirewalls(p)
|
||||
if err != nil {
|
||||
return true, nil
|
||||
}
|
||||
return len(is) == 0, nil
|
||||
}
|
||||
|
||||
// ExtractFirewalls 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 ExtractFirewalls(page pagination.Page) ([]Firewall, error) {
|
||||
var resp struct {
|
||||
Firewalls []Firewall `mapstructure:"firewalls" json:"firewalls"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(page.(FirewallPage).Body, &resp)
|
||||
|
||||
return resp.Firewalls, err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// CreateResult represents the result of a create operation.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
16
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/urls.go
generated
vendored
Normal file
16
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/urls.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package firewalls
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
const (
|
||||
rootPath = "fw"
|
||||
resourcePath = "firewalls"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
243
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/requests.go
generated
vendored
Normal file
243
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/requests.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
package policies
|
||||
|
||||
import (
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// Binary gives users a solid type to work with for create and update
|
||||
// operations. It is recommended that users use the `Yes` and `No` enums
|
||||
type Binary *bool
|
||||
|
||||
// Convenience vars for Audited and Shared values.
|
||||
var (
|
||||
iTrue = true
|
||||
iFalse = false
|
||||
Yes Binary = &iTrue
|
||||
No Binary = &iFalse
|
||||
)
|
||||
|
||||
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||
// List request.
|
||||
type ListOptsBuilder interface {
|
||||
ToPolicyListQuery() (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 firewall policy attributes you want to see returned. SortKey allows you
|
||||
// to sort by a particular firewall policy attribute. SortDir sets the direction,
|
||||
// and is either `asc' or `desc'. Marker and Limit are used for pagination.
|
||||
type ListOpts struct {
|
||||
TenantID string `q:"tenant_id"`
|
||||
Name string `q:"name"`
|
||||
Description string `q:"description"`
|
||||
Shared bool `q:"shared"`
|
||||
Audited bool `q:"audited"`
|
||||
ID string `q:"id"`
|
||||
Limit int `q:"limit"`
|
||||
Marker string `q:"marker"`
|
||||
SortKey string `q:"sort_key"`
|
||||
SortDir string `q:"sort_dir"`
|
||||
}
|
||||
|
||||
// ToPolicyListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToPolicyListQuery() (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
|
||||
// firewall policies. It accepts a ListOpts struct, which allows you to filter
|
||||
// and sort the returned collection for greater efficiency.
|
||||
//
|
||||
// Default policy settings return only those firewall policies 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.ToPolicyListQuery()
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
url += query
|
||||
}
|
||||
|
||||
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
|
||||
return PolicyPage{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 {
|
||||
ToPolicyCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts contains all the values needed to create a new firewall policy.
|
||||
type CreateOpts struct {
|
||||
// Only required if the caller has an admin role and wants to create a firewall policy
|
||||
// for another tenant.
|
||||
TenantID string
|
||||
Name string
|
||||
Description string
|
||||
Shared *bool
|
||||
Audited *bool
|
||||
Rules []string
|
||||
}
|
||||
|
||||
// ToPolicyCreateMap casts a CreateOpts struct to a map.
|
||||
func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) {
|
||||
p := make(map[string]interface{})
|
||||
|
||||
if opts.TenantID != "" {
|
||||
p["tenant_id"] = opts.TenantID
|
||||
}
|
||||
if opts.Name != "" {
|
||||
p["name"] = opts.Name
|
||||
}
|
||||
if opts.Description != "" {
|
||||
p["description"] = opts.Description
|
||||
}
|
||||
if opts.Shared != nil {
|
||||
p["shared"] = *opts.Shared
|
||||
}
|
||||
if opts.Audited != nil {
|
||||
p["audited"] = *opts.Audited
|
||||
}
|
||||
if opts.Rules != nil {
|
||||
p["firewall_rules"] = opts.Rules
|
||||
}
|
||||
|
||||
return map[string]interface{}{"firewall_policy": p}, nil
|
||||
}
|
||||
|
||||
// Create accepts a CreateOpts struct and uses the values to create a new firewall policy
|
||||
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||
var res CreateResult
|
||||
|
||||
reqBody, err := opts.ToPolicyCreateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// Get retrieves a particular firewall policy 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 {
|
||||
ToPolicyUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateOpts contains the values used when updating a firewall policy.
|
||||
type UpdateOpts struct {
|
||||
// Name of the firewall policy.
|
||||
Name string
|
||||
Description string
|
||||
Shared *bool
|
||||
Audited *bool
|
||||
Rules []string
|
||||
}
|
||||
|
||||
// ToPolicyUpdateMap casts a CreateOpts struct to a map.
|
||||
func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) {
|
||||
p := make(map[string]interface{})
|
||||
|
||||
if opts.Name != "" {
|
||||
p["name"] = opts.Name
|
||||
}
|
||||
if opts.Description != "" {
|
||||
p["description"] = opts.Description
|
||||
}
|
||||
if opts.Shared != nil {
|
||||
p["shared"] = *opts.Shared
|
||||
}
|
||||
if opts.Audited != nil {
|
||||
p["audited"] = *opts.Audited
|
||||
}
|
||||
if opts.Rules != nil {
|
||||
p["firewall_rules"] = opts.Rules
|
||||
}
|
||||
|
||||
return map[string]interface{}{"firewall_policy": p}, nil
|
||||
}
|
||||
|
||||
// Update allows firewall policies to be updated.
|
||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
|
||||
var res UpdateResult
|
||||
|
||||
reqBody, err := opts.ToPolicyUpdateMap()
|
||||
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 firewall policy 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 InsertRule(c *gophercloud.ServiceClient, policyID, ruleID, beforeID, afterID string) error {
|
||||
type request struct {
|
||||
RuleId string `json:"firewall_rule_id"`
|
||||
Before string `json:"insert_before,omitempty"`
|
||||
After string `json:"insert_after,omitempty"`
|
||||
}
|
||||
|
||||
reqBody := request{
|
||||
RuleId: ruleID,
|
||||
Before: beforeID,
|
||||
After: afterID,
|
||||
}
|
||||
|
||||
// Send request to API
|
||||
var res commonResult
|
||||
_, res.Err = c.Put(insertURL(c, policyID), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return res.Err
|
||||
}
|
||||
|
||||
func RemoveRule(c *gophercloud.ServiceClient, policyID, ruleID string) error {
|
||||
type request struct {
|
||||
RuleId string `json:"firewall_rule_id"`
|
||||
}
|
||||
|
||||
reqBody := request{
|
||||
RuleId: ruleID,
|
||||
}
|
||||
|
||||
// Send request to API
|
||||
var res commonResult
|
||||
_, res.Err = c.Put(removeURL(c, policyID), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return res.Err
|
||||
}
|
||||
279
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/requests_test.go
generated
vendored
Normal file
279
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
package policies
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestURLs(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/fw/firewall_policies", rootURL(fake.ServiceClient()))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewall_policies", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewall_policies": [
|
||||
{
|
||||
"name": "policy1",
|
||||
"firewall_rules": [
|
||||
"75452b36-268e-4e75-aaf4-f0e7ed50bc97",
|
||||
"c9e77ca0-1bc8-497d-904d-948107873dc6"
|
||||
],
|
||||
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
|
||||
"audited": true,
|
||||
"shared": false,
|
||||
"id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
|
||||
"description": "Firewall policy 1"
|
||||
},
|
||||
{
|
||||
"name": "policy2",
|
||||
"firewall_rules": [
|
||||
"03d2a6ad-633f-431a-8463-4370d06a22c8"
|
||||
],
|
||||
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
|
||||
"audited": false,
|
||||
"shared": true,
|
||||
"id": "c854fab5-bdaf-4a86-9359-78de93e5df01",
|
||||
"description": "Firewall policy 2"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractPolicies(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract members: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []Policy{
|
||||
Policy{
|
||||
Name: "policy1",
|
||||
Rules: []string{
|
||||
"75452b36-268e-4e75-aaf4-f0e7ed50bc97",
|
||||
"c9e77ca0-1bc8-497d-904d-948107873dc6",
|
||||
},
|
||||
TenantID: "9145d91459d248b1b02fdaca97c6a75d",
|
||||
Audited: true,
|
||||
Shared: false,
|
||||
ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
|
||||
Description: "Firewall policy 1",
|
||||
},
|
||||
Policy{
|
||||
Name: "policy2",
|
||||
Rules: []string{
|
||||
"03d2a6ad-633f-431a-8463-4370d06a22c8",
|
||||
},
|
||||
TenantID: "9145d91459d248b1b02fdaca97c6a75d",
|
||||
Audited: false,
|
||||
Shared: true,
|
||||
ID: "c854fab5-bdaf-4a86-9359-78de93e5df01",
|
||||
Description: "Firewall policy 2",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewall_policies", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"firewall_policy":{
|
||||
"name": "policy",
|
||||
"firewall_rules": [
|
||||
"98a58c87-76be-ae7c-a74e-b77fffb88d95",
|
||||
"11a58c87-76be-ae7c-a74e-b77fffb88a32"
|
||||
],
|
||||
"description": "Firewall policy",
|
||||
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
|
||||
"audited": true,
|
||||
"shared": false
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewall_policy":{
|
||||
"name": "policy",
|
||||
"firewall_rules": [
|
||||
"98a58c87-76be-ae7c-a74e-b77fffb88d95",
|
||||
"11a58c87-76be-ae7c-a74e-b77fffb88a32"
|
||||
],
|
||||
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
|
||||
"audited": false,
|
||||
"id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
|
||||
"description": "Firewall policy"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := CreateOpts{
|
||||
TenantID: "9145d91459d248b1b02fdaca97c6a75d",
|
||||
Name: "policy",
|
||||
Description: "Firewall policy",
|
||||
Shared: No,
|
||||
Audited: Yes,
|
||||
Rules: []string{
|
||||
"98a58c87-76be-ae7c-a74e-b77fffb88d95",
|
||||
"11a58c87-76be-ae7c-a74e-b77fffb88a32",
|
||||
},
|
||||
}
|
||||
|
||||
_, err := Create(fake.ServiceClient(), options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewall_policies/bcab5315-64f6-4ea3-8e58-981cc37c6f61", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewall_policy":{
|
||||
"name": "www",
|
||||
"firewall_rules": [
|
||||
"75452b36-268e-4e75-aaf4-f0e7ed50bc97",
|
||||
"c9e77ca0-1bc8-497d-904d-948107873dc6",
|
||||
"03d2a6ad-633f-431a-8463-4370d06a22c8"
|
||||
],
|
||||
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
|
||||
"audited": false,
|
||||
"id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
|
||||
"description": "Firewall policy web"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
policy, err := Get(fake.ServiceClient(), "bcab5315-64f6-4ea3-8e58-981cc37c6f61").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "www", policy.Name)
|
||||
th.AssertEquals(t, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", policy.ID)
|
||||
th.AssertEquals(t, "Firewall policy web", policy.Description)
|
||||
th.AssertEquals(t, 3, len(policy.Rules))
|
||||
th.AssertEquals(t, "75452b36-268e-4e75-aaf4-f0e7ed50bc97", policy.Rules[0])
|
||||
th.AssertEquals(t, "c9e77ca0-1bc8-497d-904d-948107873dc6", policy.Rules[1])
|
||||
th.AssertEquals(t, "03d2a6ad-633f-431a-8463-4370d06a22c8", policy.Rules[2])
|
||||
th.AssertEquals(t, "9145d91459d248b1b02fdaca97c6a75d", policy.TenantID)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewall_policies/f2b08c1e-aa81-4668-8ae1-1401bcb0576c", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"firewall_policy":{
|
||||
"name": "policy",
|
||||
"firewall_rules": [
|
||||
"98a58c87-76be-ae7c-a74e-b77fffb88d95",
|
||||
"11a58c87-76be-ae7c-a74e-b77fffb88a32"
|
||||
],
|
||||
"description": "Firewall policy"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewall_policy":{
|
||||
"name": "policy",
|
||||
"firewall_rules": [
|
||||
"75452b36-268e-4e75-aaf4-f0e7ed50bc97",
|
||||
"c9e77ca0-1bc8-497d-904d-948107873dc6",
|
||||
"03d2a6ad-633f-431a-8463-4370d06a22c8"
|
||||
],
|
||||
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
|
||||
"audited": false,
|
||||
"id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
|
||||
"description": "Firewall policy"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := UpdateOpts{
|
||||
Name: "policy",
|
||||
Description: "Firewall policy",
|
||||
Rules: []string{
|
||||
"98a58c87-76be-ae7c-a74e-b77fffb88d95",
|
||||
"11a58c87-76be-ae7c-a74e-b77fffb88a32",
|
||||
},
|
||||
}
|
||||
|
||||
_, err := Update(fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewall_policies/4ec89077-d057-4a2b-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
101
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/results.go
generated
vendored
Normal file
101
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/results.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
package policies
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
type Policy struct {
|
||||
ID string `json:"id" mapstructure:"id"`
|
||||
Name string `json:"name" mapstructure:"name"`
|
||||
Description string `json:"description" mapstructure:"description"`
|
||||
TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
|
||||
Audited bool `json:"audited" mapstructure:"audited"`
|
||||
Shared bool `json:"shared" mapstructure:"shared"`
|
||||
Rules []string `json:"firewall_rules,omitempty" mapstructure:"firewall_rules"`
|
||||
}
|
||||
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a function that accepts a result and extracts a firewall policy.
|
||||
func (r commonResult) Extract() (*Policy, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Policy *Policy `json:"firewall_policy" mapstructure:"firewall_policy"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &res)
|
||||
|
||||
return res.Policy, err
|
||||
}
|
||||
|
||||
// PolicyPage is the page returned by a pager when traversing over a
|
||||
// collection of firewall policies.
|
||||
type PolicyPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// NextPageURL is invoked when a paginated collection of firewall policies 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 PolicyPage) NextPageURL() (string, error) {
|
||||
type resp struct {
|
||||
Links []gophercloud.Link `mapstructure:"firewall_policies_links"`
|
||||
}
|
||||
|
||||
var r resp
|
||||
err := mapstructure.Decode(p.Body, &r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return gophercloud.ExtractNextURL(r.Links)
|
||||
}
|
||||
|
||||
// IsEmpty checks whether a PolicyPage struct is empty.
|
||||
func (p PolicyPage) IsEmpty() (bool, error) {
|
||||
is, err := ExtractPolicies(p)
|
||||
if err != nil {
|
||||
return true, nil
|
||||
}
|
||||
return len(is) == 0, nil
|
||||
}
|
||||
|
||||
// ExtractPolicies 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 ExtractPolicies(page pagination.Page) ([]Policy, error) {
|
||||
var resp struct {
|
||||
Policies []Policy `mapstructure:"firewall_policies" json:"firewall_policies"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(page.(PolicyPage).Body, &resp)
|
||||
|
||||
return resp.Policies, err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// CreateResult represents the result of a create operation.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
26
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/urls.go
generated
vendored
Normal file
26
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/urls.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package policies
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
const (
|
||||
rootPath = "fw"
|
||||
resourcePath = "firewall_policies"
|
||||
insertPath = "insert_rule"
|
||||
removePath = "remove_rule"
|
||||
)
|
||||
|
||||
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 insertURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL(rootPath, resourcePath, id, insertPath)
|
||||
}
|
||||
|
||||
func removeURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL(rootPath, resourcePath, id, removePath)
|
||||
}
|
||||
12
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/errors.go
generated
vendored
Normal file
12
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/errors.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package rules
|
||||
|
||||
import "fmt"
|
||||
|
||||
func err(str string) error {
|
||||
return fmt.Errorf("%s", str)
|
||||
}
|
||||
|
||||
var (
|
||||
errProtocolRequired = err("A protocol is required (tcp, udp, icmp or any)")
|
||||
errActionRequired = err("An action is required (allow or deny)")
|
||||
)
|
||||
285
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/requests.go
generated
vendored
Normal file
285
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/requests.go
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// Binary gives users a solid type to work with for create and update
|
||||
// operations. It is recommended that users use the `Yes` and `No` enums
|
||||
type Binary *bool
|
||||
|
||||
// Convenience vars for Enabled and Shared values.
|
||||
var (
|
||||
iTrue = true
|
||||
iFalse = false
|
||||
Yes Binary = &iTrue
|
||||
No Binary = &iFalse
|
||||
)
|
||||
|
||||
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||
// List request.
|
||||
type ListOptsBuilder interface {
|
||||
ToRuleListQuery() (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 Firewall rule attributes you want to see returned. SortKey allows you to
|
||||
// sort by a particular firewall rule attribute. SortDir sets the direction, and is
|
||||
// either `asc' or `desc'. Marker and Limit are used for pagination.
|
||||
type ListOpts struct {
|
||||
TenantID string `q:"tenant_id"`
|
||||
Name string `q:"name"`
|
||||
Description string `q:"description"`
|
||||
Protocol string `q:"protocol"`
|
||||
Action string `q:"action"`
|
||||
IPVersion int `q:"ip_version"`
|
||||
SourceIPAddress string `q:"source_ip_address"`
|
||||
DestinationIPAddress string `q:"destination_ip_address"`
|
||||
SourcePort string `q:"source_port"`
|
||||
DestinationPort string `q:"destination_port"`
|
||||
Enabled bool `q:"enabled"`
|
||||
ID string `q:"id"`
|
||||
Limit int `q:"limit"`
|
||||
Marker string `q:"marker"`
|
||||
SortKey string `q:"sort_key"`
|
||||
SortDir string `q:"sort_dir"`
|
||||
}
|
||||
|
||||
// ToRuleListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToRuleListQuery() (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
|
||||
// firewall rules. It accepts a ListOpts struct, which allows you to filter
|
||||
// and sort the returned collection for greater efficiency.
|
||||
//
|
||||
// Default policy settings return only those firewall rules 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.ToRuleListQuery()
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
url += query
|
||||
}
|
||||
|
||||
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
|
||||
return RulePage{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 {
|
||||
ToRuleCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts contains all the values needed to create a new firewall rule.
|
||||
type CreateOpts struct {
|
||||
// Mandatory for create
|
||||
Protocol string
|
||||
Action string
|
||||
// Optional
|
||||
TenantID string
|
||||
Name string
|
||||
Description string
|
||||
IPVersion int
|
||||
SourceIPAddress string
|
||||
DestinationIPAddress string
|
||||
SourcePort string
|
||||
DestinationPort string
|
||||
Shared *bool
|
||||
Enabled *bool
|
||||
}
|
||||
|
||||
// ToRuleCreateMap casts a CreateOpts struct to a map.
|
||||
func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
|
||||
if opts.Protocol == "" {
|
||||
return nil, errProtocolRequired
|
||||
}
|
||||
|
||||
if opts.Action == "" {
|
||||
return nil, errActionRequired
|
||||
}
|
||||
|
||||
r := make(map[string]interface{})
|
||||
|
||||
r["protocol"] = opts.Protocol
|
||||
r["action"] = opts.Action
|
||||
|
||||
if opts.TenantID != "" {
|
||||
r["tenant_id"] = opts.TenantID
|
||||
}
|
||||
if opts.Name != "" {
|
||||
r["name"] = opts.Name
|
||||
}
|
||||
if opts.Description != "" {
|
||||
r["description"] = opts.Description
|
||||
}
|
||||
if opts.IPVersion != 0 {
|
||||
r["ip_version"] = opts.IPVersion
|
||||
}
|
||||
if opts.SourceIPAddress != "" {
|
||||
r["source_ip_address"] = opts.SourceIPAddress
|
||||
}
|
||||
if opts.DestinationIPAddress != "" {
|
||||
r["destination_ip_address"] = opts.DestinationIPAddress
|
||||
}
|
||||
if opts.SourcePort != "" {
|
||||
r["source_port"] = opts.SourcePort
|
||||
}
|
||||
if opts.DestinationPort != "" {
|
||||
r["destination_port"] = opts.DestinationPort
|
||||
}
|
||||
if opts.Shared != nil {
|
||||
r["shared"] = *opts.Shared
|
||||
}
|
||||
if opts.Enabled != nil {
|
||||
r["enabled"] = *opts.Enabled
|
||||
}
|
||||
|
||||
return map[string]interface{}{"firewall_rule": r}, nil
|
||||
}
|
||||
|
||||
// Create accepts a CreateOpts struct and uses the values to create a new firewall rule
|
||||
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||
var res CreateResult
|
||||
|
||||
reqBody, err := opts.ToRuleCreateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// Get retrieves a particular firewall 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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
ToRuleUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateOpts contains the values used when updating a firewall rule.
|
||||
// Optional
|
||||
type UpdateOpts struct {
|
||||
Protocol string
|
||||
Action string
|
||||
Name string
|
||||
Description string
|
||||
IPVersion int
|
||||
SourceIPAddress *string
|
||||
DestinationIPAddress *string
|
||||
SourcePort *string
|
||||
DestinationPort *string
|
||||
Shared *bool
|
||||
Enabled *bool
|
||||
}
|
||||
|
||||
// ToRuleUpdateMap casts a UpdateOpts struct to a map.
|
||||
func (opts UpdateOpts) ToRuleUpdateMap() (map[string]interface{}, error) {
|
||||
r := make(map[string]interface{})
|
||||
|
||||
if opts.Protocol != "" {
|
||||
r["protocol"] = opts.Protocol
|
||||
}
|
||||
if opts.Action != "" {
|
||||
r["action"] = opts.Action
|
||||
}
|
||||
if opts.Name != "" {
|
||||
r["name"] = opts.Name
|
||||
}
|
||||
if opts.Description != "" {
|
||||
r["description"] = opts.Description
|
||||
}
|
||||
if opts.IPVersion != 0 {
|
||||
r["ip_version"] = opts.IPVersion
|
||||
}
|
||||
if opts.SourceIPAddress != nil {
|
||||
s := *opts.SourceIPAddress
|
||||
if s == "" {
|
||||
r["source_ip_address"] = nil
|
||||
} else {
|
||||
r["source_ip_address"] = s
|
||||
}
|
||||
}
|
||||
if opts.DestinationIPAddress != nil {
|
||||
s := *opts.DestinationIPAddress
|
||||
if s == "" {
|
||||
r["destination_ip_address"] = nil
|
||||
} else {
|
||||
r["destination_ip_address"] = s
|
||||
}
|
||||
}
|
||||
if opts.SourcePort != nil {
|
||||
s := *opts.SourcePort
|
||||
if s == "" {
|
||||
r["source_port"] = nil
|
||||
} else {
|
||||
r["source_port"] = s
|
||||
}
|
||||
}
|
||||
if opts.DestinationPort != nil {
|
||||
s := *opts.DestinationPort
|
||||
if s == "" {
|
||||
r["destination_port"] = nil
|
||||
} else {
|
||||
r["destination_port"] = s
|
||||
}
|
||||
}
|
||||
if opts.Shared != nil {
|
||||
r["shared"] = *opts.Shared
|
||||
}
|
||||
if opts.Enabled != nil {
|
||||
r["enabled"] = *opts.Enabled
|
||||
}
|
||||
|
||||
return map[string]interface{}{"firewall_rule": r}, nil
|
||||
}
|
||||
|
||||
// Update allows firewall policies to be updated.
|
||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
|
||||
var res UpdateResult
|
||||
|
||||
reqBody, err := opts.ToRuleUpdateMap()
|
||||
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 firewall 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
|
||||
}
|
||||
328
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/requests_test.go
generated
vendored
Normal file
328
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,328 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestURLs(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/fw/firewall_rules", rootURL(fake.ServiceClient()))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewall_rules": [
|
||||
{
|
||||
"protocol": "tcp",
|
||||
"description": "ssh rule",
|
||||
"source_port": null,
|
||||
"source_ip_address": null,
|
||||
"destination_ip_address": "192.168.1.0/24",
|
||||
"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
|
||||
"position": 2,
|
||||
"destination_port": "22",
|
||||
"id": "f03bd950-6c56-4f5e-a307-45967078f507",
|
||||
"name": "ssh_form_any",
|
||||
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
|
||||
"enabled": true,
|
||||
"action": "allow",
|
||||
"ip_version": 4,
|
||||
"shared": false
|
||||
},
|
||||
{
|
||||
"protocol": "udp",
|
||||
"description": "udp rule",
|
||||
"source_port": null,
|
||||
"source_ip_address": null,
|
||||
"destination_ip_address": null,
|
||||
"firewall_policy_id": "98d7fb51-698c-4123-87e8-f1eee6b5ab7e",
|
||||
"position": 1,
|
||||
"destination_port": null,
|
||||
"id": "ab7bd950-6c56-4f5e-a307-45967078f890",
|
||||
"name": "deny_all_udp",
|
||||
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
|
||||
"enabled": true,
|
||||
"action": "deny",
|
||||
"ip_version": 4,
|
||||
"shared": false
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractRules(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract members: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []Rule{
|
||||
Rule{
|
||||
Protocol: "tcp",
|
||||
Description: "ssh rule",
|
||||
SourcePort: "",
|
||||
SourceIPAddress: "",
|
||||
DestinationIPAddress: "192.168.1.0/24",
|
||||
PolicyID: "e2a5fb51-698c-4898-87e8-f1eee6b50919",
|
||||
Position: 2,
|
||||
DestinationPort: "22",
|
||||
ID: "f03bd950-6c56-4f5e-a307-45967078f507",
|
||||
Name: "ssh_form_any",
|
||||
TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61",
|
||||
Enabled: true,
|
||||
Action: "allow",
|
||||
IPVersion: 4,
|
||||
Shared: false,
|
||||
},
|
||||
Rule{
|
||||
Protocol: "udp",
|
||||
Description: "udp rule",
|
||||
SourcePort: "",
|
||||
SourceIPAddress: "",
|
||||
DestinationIPAddress: "",
|
||||
PolicyID: "98d7fb51-698c-4123-87e8-f1eee6b5ab7e",
|
||||
Position: 1,
|
||||
DestinationPort: "",
|
||||
ID: "ab7bd950-6c56-4f5e-a307-45967078f890",
|
||||
Name: "deny_all_udp",
|
||||
TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61",
|
||||
Enabled: true,
|
||||
Action: "deny",
|
||||
IPVersion: 4,
|
||||
Shared: false,
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"firewall_rule": {
|
||||
"protocol": "tcp",
|
||||
"description": "ssh rule",
|
||||
"destination_ip_address": "192.168.1.0/24",
|
||||
"destination_port": "22",
|
||||
"name": "ssh_form_any",
|
||||
"action": "allow",
|
||||
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewall_rule":{
|
||||
"protocol": "tcp",
|
||||
"description": "ssh rule",
|
||||
"source_port": null,
|
||||
"source_ip_address": null,
|
||||
"destination_ip_address": "192.168.1.0/24",
|
||||
"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
|
||||
"position": 2,
|
||||
"destination_port": "22",
|
||||
"id": "f03bd950-6c56-4f5e-a307-45967078f507",
|
||||
"name": "ssh_form_any",
|
||||
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
|
||||
"enabled": true,
|
||||
"action": "allow",
|
||||
"ip_version": 4,
|
||||
"shared": false
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := CreateOpts{
|
||||
TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61",
|
||||
Protocol: "tcp",
|
||||
Description: "ssh rule",
|
||||
DestinationIPAddress: "192.168.1.0/24",
|
||||
DestinationPort: "22",
|
||||
Name: "ssh_form_any",
|
||||
Action: "allow",
|
||||
}
|
||||
|
||||
_, err := Create(fake.ServiceClient(), options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewall_rule":{
|
||||
"protocol": "tcp",
|
||||
"description": "ssh rule",
|
||||
"source_port": null,
|
||||
"source_ip_address": null,
|
||||
"destination_ip_address": "192.168.1.0/24",
|
||||
"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
|
||||
"position": 2,
|
||||
"destination_port": "22",
|
||||
"id": "f03bd950-6c56-4f5e-a307-45967078f507",
|
||||
"name": "ssh_form_any",
|
||||
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
|
||||
"enabled": true,
|
||||
"action": "allow",
|
||||
"ip_version": 4,
|
||||
"shared": false
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
rule, err := Get(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "tcp", rule.Protocol)
|
||||
th.AssertEquals(t, "ssh rule", rule.Description)
|
||||
th.AssertEquals(t, "192.168.1.0/24", rule.DestinationIPAddress)
|
||||
th.AssertEquals(t, "e2a5fb51-698c-4898-87e8-f1eee6b50919", rule.PolicyID)
|
||||
th.AssertEquals(t, 2, rule.Position)
|
||||
th.AssertEquals(t, "22", rule.DestinationPort)
|
||||
th.AssertEquals(t, "f03bd950-6c56-4f5e-a307-45967078f507", rule.ID)
|
||||
th.AssertEquals(t, "ssh_form_any", rule.Name)
|
||||
th.AssertEquals(t, "80cf934d6ffb4ef5b244f1c512ad1e61", rule.TenantID)
|
||||
th.AssertEquals(t, true, rule.Enabled)
|
||||
th.AssertEquals(t, "allow", rule.Action)
|
||||
th.AssertEquals(t, 4, rule.IPVersion)
|
||||
th.AssertEquals(t, false, rule.Shared)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"firewall_rule":{
|
||||
"protocol": "tcp",
|
||||
"description": "ssh rule",
|
||||
"destination_ip_address": "192.168.1.0/24",
|
||||
"destination_port": "22",
|
||||
"source_ip_address": null,
|
||||
"source_port": null,
|
||||
"name": "ssh_form_any",
|
||||
"action": "allow",
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"firewall_rule":{
|
||||
"protocol": "tcp",
|
||||
"description": "ssh rule",
|
||||
"source_port": null,
|
||||
"source_ip_address": null,
|
||||
"destination_ip_address": "192.168.1.0/24",
|
||||
"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
|
||||
"position": 2,
|
||||
"destination_port": "22",
|
||||
"id": "f03bd950-6c56-4f5e-a307-45967078f507",
|
||||
"name": "ssh_form_any",
|
||||
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
|
||||
"enabled": false,
|
||||
"action": "allow",
|
||||
"ip_version": 4,
|
||||
"shared": false
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
destinationIPAddress := "192.168.1.0/24"
|
||||
destinationPort := "22"
|
||||
empty := ""
|
||||
|
||||
options := UpdateOpts{
|
||||
Protocol: "tcp",
|
||||
Description: "ssh rule",
|
||||
DestinationIPAddress: &destinationIPAddress,
|
||||
DestinationPort: &destinationPort,
|
||||
Name: "ssh_form_any",
|
||||
SourceIPAddress: &empty,
|
||||
SourcePort: &empty,
|
||||
Action: "allow",
|
||||
Enabled: No,
|
||||
}
|
||||
|
||||
_, err := Update(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/fw/firewall_rules/4ec89077-d057-4a2b-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
110
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/results.go
generated
vendored
Normal file
110
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/results.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// Rule represents a firewall rule
|
||||
type Rule struct {
|
||||
ID string `json:"id" mapstructure:"id"`
|
||||
Name string `json:"name,omitempty" mapstructure:"name"`
|
||||
Description string `json:"description,omitempty" mapstructure:"description"`
|
||||
Protocol string `json:"protocol" mapstructure:"protocol"`
|
||||
Action string `json:"action" mapstructure:"action"`
|
||||
IPVersion int `json:"ip_version,omitempty" mapstructure:"ip_version"`
|
||||
SourceIPAddress string `json:"source_ip_address,omitempty" mapstructure:"source_ip_address"`
|
||||
DestinationIPAddress string `json:"destination_ip_address,omitempty" mapstructure:"destination_ip_address"`
|
||||
SourcePort string `json:"source_port,omitempty" mapstructure:"source_port"`
|
||||
DestinationPort string `json:"destination_port,omitempty" mapstructure:"destination_port"`
|
||||
Shared bool `json:"shared,omitempty" mapstructure:"shared"`
|
||||
Enabled bool `json:"enabled,omitempty" mapstructure:"enabled"`
|
||||
PolicyID string `json:"firewall_policy_id" mapstructure:"firewall_policy_id"`
|
||||
Position int `json:"position" mapstructure:"position"`
|
||||
TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
|
||||
}
|
||||
|
||||
// RulePage is the page returned by a pager when traversing over a
|
||||
// collection of firewall rules.
|
||||
type RulePage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// NextPageURL is invoked when a paginated collection of firewall 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 RulePage) NextPageURL() (string, error) {
|
||||
type resp struct {
|
||||
Links []gophercloud.Link `mapstructure:"firewall_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 RulePage struct is empty.
|
||||
func (p RulePage) 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 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 ExtractRules(page pagination.Page) ([]Rule, error) {
|
||||
var resp struct {
|
||||
Rules []Rule `mapstructure:"firewall_rules" json:"firewall_rules"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(page.(RulePage).Body, &resp)
|
||||
|
||||
return resp.Rules, err
|
||||
}
|
||||
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a function that accepts a result and extracts a firewall rule.
|
||||
func (r commonResult) Extract() (*Rule, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Rule *Rule `json:"firewall_rule" mapstructure:"firewall_rule"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &res)
|
||||
|
||||
return res.Rule, err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// CreateResult represents the result of a create operation.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
16
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/urls.go
generated
vendored
Normal file
16
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/urls.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package rules
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
const (
|
||||
rootPath = "fw"
|
||||
resourcePath = "firewall_rules"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
5
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/doc.go
generated
vendored
Normal file
5
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/doc.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Package layer3 provides access to the Layer-3 networking extension for the
|
||||
// OpenStack Neutron service. This extension allows API users to route packets
|
||||
// between subnets, forward packets from internal networks to external ones,
|
||||
// and access instances from external networks through floating IPs.
|
||||
package layer3
|
||||
167
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests.go
generated
vendored
Normal file
167
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests.go
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
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,
|
||||
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
|
||||
}
|
||||
355
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests_test.go
generated
vendored
Normal file
355
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
package floatingips
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"floatingips": [
|
||||
{
|
||||
"floating_network_id": "6d67c30a-ddb4-49a1-bec3-a65b286b4170",
|
||||
"router_id": null,
|
||||
"fixed_ip_address": null,
|
||||
"floating_ip_address": "192.0.0.4",
|
||||
"tenant_id": "017d8de156df4177889f31a9bd6edc00",
|
||||
"status": "DOWN",
|
||||
"port_id": null,
|
||||
"id": "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e"
|
||||
},
|
||||
{
|
||||
"floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64",
|
||||
"router_id": "0a24cb83-faf5-4d7f-b723-3144ed8a2167",
|
||||
"fixed_ip_address": "192.0.0.2",
|
||||
"floating_ip_address": "10.0.0.3",
|
||||
"tenant_id": "017d8de156df4177889f31a9bd6edc00",
|
||||
"status": "DOWN",
|
||||
"port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25",
|
||||
"id": "ada25a95-f321-4f59-b0e0-f3a970dd3d63"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractFloatingIPs(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract floating IPs: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []FloatingIP{
|
||||
FloatingIP{
|
||||
FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170",
|
||||
FixedIP: "",
|
||||
FloatingIP: "192.0.0.4",
|
||||
TenantID: "017d8de156df4177889f31a9bd6edc00",
|
||||
Status: "DOWN",
|
||||
PortID: "",
|
||||
ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e",
|
||||
},
|
||||
FloatingIP{
|
||||
FloatingNetworkID: "90f742b1-6d17-487b-ba95-71881dbc0b64",
|
||||
FixedIP: "192.0.0.2",
|
||||
FloatingIP: "10.0.0.3",
|
||||
TenantID: "017d8de156df4177889f31a9bd6edc00",
|
||||
Status: "DOWN",
|
||||
PortID: "74a342ce-8e07-4e91-880c-9f834b68fa25",
|
||||
ID: "ada25a95-f321-4f59-b0e0-f3a970dd3d63",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidNextPageURLs(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, `{"floatingips": [{}], "floatingips_links": {}}`)
|
||||
})
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
ExtractFloatingIPs(page)
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequiredFieldsForCreate(t *testing.T) {
|
||||
res1 := Create(fake.ServiceClient(), CreateOpts{FloatingNetworkID: ""})
|
||||
if res1.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
|
||||
res2 := Create(fake.ServiceClient(), CreateOpts{FloatingNetworkID: "foo", PortID: ""})
|
||||
if res2.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"floatingip": {
|
||||
"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
|
||||
"port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"floatingip": {
|
||||
"router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
|
||||
"tenant_id": "4969c491a3c74ee4af974e6d800c62de",
|
||||
"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
|
||||
"fixed_ip_address": "10.0.0.3",
|
||||
"floating_ip_address": "",
|
||||
"port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab",
|
||||
"id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := CreateOpts{
|
||||
FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57",
|
||||
PortID: "ce705c24-c1ef-408a-bda3-7bbd946164ab",
|
||||
}
|
||||
|
||||
ip, err := Create(fake.ServiceClient(), options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID)
|
||||
th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID)
|
||||
th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID)
|
||||
th.AssertEquals(t, "", ip.FloatingIP)
|
||||
th.AssertEquals(t, "ce705c24-c1ef-408a-bda3-7bbd946164ab", ip.PortID)
|
||||
th.AssertEquals(t, "10.0.0.3", ip.FixedIP)
|
||||
}
|
||||
|
||||
func TestCreateEmptyPort(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"floatingip": {
|
||||
"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"floatingip": {
|
||||
"router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
|
||||
"tenant_id": "4969c491a3c74ee4af974e6d800c62de",
|
||||
"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
|
||||
"fixed_ip_address": "10.0.0.3",
|
||||
"floating_ip_address": "",
|
||||
"id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := CreateOpts{
|
||||
FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57",
|
||||
}
|
||||
|
||||
ip, err := Create(fake.ServiceClient(), options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID)
|
||||
th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID)
|
||||
th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID)
|
||||
th.AssertEquals(t, "", ip.FloatingIP)
|
||||
th.AssertEquals(t, "", ip.PortID)
|
||||
th.AssertEquals(t, "10.0.0.3", ip.FixedIP)
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"floatingip": {
|
||||
"floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64",
|
||||
"fixed_ip_address": "192.0.0.2",
|
||||
"floating_ip_address": "10.0.0.3",
|
||||
"tenant_id": "017d8de156df4177889f31a9bd6edc00",
|
||||
"status": "DOWN",
|
||||
"port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25",
|
||||
"id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
ip, err := Get(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "90f742b1-6d17-487b-ba95-71881dbc0b64", ip.FloatingNetworkID)
|
||||
th.AssertEquals(t, "10.0.0.3", ip.FloatingIP)
|
||||
th.AssertEquals(t, "74a342ce-8e07-4e91-880c-9f834b68fa25", ip.PortID)
|
||||
th.AssertEquals(t, "192.0.0.2", ip.FixedIP)
|
||||
th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", ip.TenantID)
|
||||
th.AssertEquals(t, "DOWN", ip.Status)
|
||||
th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID)
|
||||
}
|
||||
|
||||
func TestAssociate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"floatingip": {
|
||||
"port_id": "423abc8d-2991-4a55-ba98-2aaea84cc72e"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"floatingip": {
|
||||
"router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
|
||||
"tenant_id": "4969c491a3c74ee4af974e6d800c62de",
|
||||
"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
|
||||
"fixed_ip_address": null,
|
||||
"floating_ip_address": "172.24.4.228",
|
||||
"port_id": "423abc8d-2991-4a55-ba98-2aaea84cc72e",
|
||||
"id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
ip, err := Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", UpdateOpts{PortID: "423abc8d-2991-4a55-ba98-2aaea84cc72e"}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertDeepEquals(t, "423abc8d-2991-4a55-ba98-2aaea84cc72e", ip.PortID)
|
||||
}
|
||||
|
||||
func TestDisassociate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"floatingip": {
|
||||
"port_id": null
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"floatingip": {
|
||||
"router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
|
||||
"tenant_id": "4969c491a3c74ee4af974e6d800c62de",
|
||||
"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
|
||||
"fixed_ip_address": null,
|
||||
"floating_ip_address": "172.24.4.228",
|
||||
"port_id": null,
|
||||
"id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
ip, err := Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", UpdateOpts{}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertDeepEquals(t, "", ip.FixedIP)
|
||||
th.AssertDeepEquals(t, "", ip.PortID)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
127
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/results.go
generated
vendored
Normal file
127
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/results.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
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
|
||||
}
|
||||
13
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/urls.go
generated
vendored
Normal file
13
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/urls.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
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)
|
||||
}
|
||||
224
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go
generated
vendored
Normal file
224
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
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"`
|
||||
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}}
|
||||
})
|
||||
}
|
||||
|
||||
// CreateOpts contains all the values needed to create a new router. There are
|
||||
// no required values.
|
||||
type CreateOpts struct {
|
||||
Name string
|
||||
AdminStateUp *bool
|
||||
TenantID string
|
||||
GatewayInfo *GatewayInfo
|
||||
}
|
||||
|
||||
// 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 CreateOpts) CreateResult {
|
||||
type router struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
||||
TenantID *string `json:"tenant_id,omitempty"`
|
||||
GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"`
|
||||
}
|
||||
|
||||
type request struct {
|
||||
Router router `json:"router"`
|
||||
}
|
||||
|
||||
reqBody := request{Router: router{
|
||||
Name: gophercloud.MaybeString(opts.Name),
|
||||
AdminStateUp: opts.AdminStateUp,
|
||||
TenantID: gophercloud.MaybeString(opts.TenantID),
|
||||
}}
|
||||
|
||||
if opts.GatewayInfo != nil {
|
||||
reqBody.Router.GatewayInfo = opts.GatewayInfo
|
||||
}
|
||||
|
||||
var res CreateResult
|
||||
_, 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
|
||||
GatewayInfo *GatewayInfo
|
||||
}
|
||||
|
||||
// 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"`
|
||||
GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"`
|
||||
}
|
||||
|
||||
type request struct {
|
||||
Router router `json:"router"`
|
||||
}
|
||||
|
||||
reqBody := request{Router: router{
|
||||
Name: gophercloud.MaybeString(opts.Name),
|
||||
AdminStateUp: opts.AdminStateUp,
|
||||
}}
|
||||
|
||||
if opts.GatewayInfo != nil {
|
||||
reqBody.Router.GatewayInfo = opts.GatewayInfo
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
338
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests_test.go
generated
vendored
Normal file
338
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,338 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestURLs(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/routers", rootURL(fake.ServiceClient()))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/routers", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"routers": [
|
||||
{
|
||||
"status": "ACTIVE",
|
||||
"external_gateway_info": null,
|
||||
"name": "second_routers",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
|
||||
"id": "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b"
|
||||
},
|
||||
{
|
||||
"status": "ACTIVE",
|
||||
"external_gateway_info": {
|
||||
"network_id": "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8"
|
||||
},
|
||||
"name": "router1",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "33a40233088643acb66ff6eb0ebea679",
|
||||
"id": "a9254bdb-2613-4a13-ac4c-adc581fba50d"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractRouters(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract routers: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []Router{
|
||||
Router{
|
||||
Status: "ACTIVE",
|
||||
GatewayInfo: GatewayInfo{NetworkID: ""},
|
||||
AdminStateUp: true,
|
||||
Name: "second_routers",
|
||||
ID: "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b",
|
||||
TenantID: "6b96ff0cb17a4b859e1e575d221683d3",
|
||||
},
|
||||
Router{
|
||||
Status: "ACTIVE",
|
||||
GatewayInfo: GatewayInfo{NetworkID: "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8"},
|
||||
AdminStateUp: true,
|
||||
Name: "router1",
|
||||
ID: "a9254bdb-2613-4a13-ac4c-adc581fba50d",
|
||||
TenantID: "33a40233088643acb66ff6eb0ebea679",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/routers", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"router":{
|
||||
"name": "foo_router",
|
||||
"admin_state_up": false,
|
||||
"external_gateway_info":{
|
||||
"network_id":"8ca37218-28ff-41cb-9b10-039601ea7e6b"
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"router": {
|
||||
"status": "ACTIVE",
|
||||
"external_gateway_info": {
|
||||
"network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
|
||||
},
|
||||
"name": "foo_router",
|
||||
"admin_state_up": false,
|
||||
"tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
|
||||
"id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
asu := false
|
||||
gwi := GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"}
|
||||
|
||||
options := CreateOpts{
|
||||
Name: "foo_router",
|
||||
AdminStateUp: &asu,
|
||||
GatewayInfo: &gwi,
|
||||
}
|
||||
r, err := Create(fake.ServiceClient(), options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "foo_router", r.Name)
|
||||
th.AssertEquals(t, false, r.AdminStateUp)
|
||||
th.AssertDeepEquals(t, GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"}, r.GatewayInfo)
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/routers/a07eea83-7710-4860-931b-5fe220fae533", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"router": {
|
||||
"status": "ACTIVE",
|
||||
"external_gateway_info": {
|
||||
"network_id": "85d76829-6415-48ff-9c63-5c5ca8c61ac6"
|
||||
},
|
||||
"name": "router1",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "d6554fe62e2f41efbb6e026fad5c1542",
|
||||
"id": "a07eea83-7710-4860-931b-5fe220fae533"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
n, err := Get(fake.ServiceClient(), "a07eea83-7710-4860-931b-5fe220fae533").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, n.Status, "ACTIVE")
|
||||
th.AssertDeepEquals(t, n.GatewayInfo, GatewayInfo{NetworkID: "85d76829-6415-48ff-9c63-5c5ca8c61ac6"})
|
||||
th.AssertEquals(t, n.Name, "router1")
|
||||
th.AssertEquals(t, n.AdminStateUp, true)
|
||||
th.AssertEquals(t, n.TenantID, "d6554fe62e2f41efbb6e026fad5c1542")
|
||||
th.AssertEquals(t, n.ID, "a07eea83-7710-4860-931b-5fe220fae533")
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"router": {
|
||||
"name": "new_name",
|
||||
"external_gateway_info": {
|
||||
"network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"router": {
|
||||
"status": "ACTIVE",
|
||||
"external_gateway_info": {
|
||||
"network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
|
||||
},
|
||||
"name": "new_name",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
|
||||
"id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
gwi := GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"}
|
||||
options := UpdateOpts{Name: "new_name", GatewayInfo: &gwi}
|
||||
|
||||
n, err := Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, n.Name, "new_name")
|
||||
th.AssertDeepEquals(t, n.GatewayInfo, GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"})
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
|
||||
func TestAddInterface(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/add_router_interface", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1"
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188",
|
||||
"tenant_id": "017d8de156df4177889f31a9bd6edc00",
|
||||
"port_id": "3f990102-4485-4df1-97a0-2c35bdb85b31",
|
||||
"id": "9a83fa11-8da5-436e-9afe-3d3ac5ce7770"
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
opts := InterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"}
|
||||
res, err := AddInterface(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID)
|
||||
th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", res.TenantID)
|
||||
th.AssertEquals(t, "3f990102-4485-4df1-97a0-2c35bdb85b31", res.PortID)
|
||||
th.AssertEquals(t, "9a83fa11-8da5-436e-9afe-3d3ac5ce7770", res.ID)
|
||||
}
|
||||
|
||||
func TestAddInterfaceRequiredOpts(t *testing.T) {
|
||||
_, err := AddInterface(fake.ServiceClient(), "foo", InterfaceOpts{}).Extract()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
_, err = AddInterface(fake.ServiceClient(), "foo", InterfaceOpts{SubnetID: "bar", PortID: "baz"}).Extract()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveInterface(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/remove_router_interface", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1"
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188",
|
||||
"tenant_id": "017d8de156df4177889f31a9bd6edc00",
|
||||
"port_id": "3f990102-4485-4df1-97a0-2c35bdb85b31",
|
||||
"id": "9a83fa11-8da5-436e-9afe-3d3ac5ce7770"
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
opts := InterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"}
|
||||
res, err := RemoveInterface(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID)
|
||||
th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", res.TenantID)
|
||||
th.AssertEquals(t, "3f990102-4485-4df1-97a0-2c35bdb85b31", res.PortID)
|
||||
th.AssertEquals(t, "9a83fa11-8da5-436e-9afe-3d3ac5ce7770", res.ID)
|
||||
}
|
||||
161
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/results.go
generated
vendored
Normal file
161
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/results.go
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
21
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/urls.go
generated
vendored
Normal file
21
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/urls.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
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")
|
||||
}
|
||||
3
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/doc.go
generated
vendored
Normal file
3
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package lbaas provides information and interaction with the Load Balancer
|
||||
// as a Service extension for the OpenStack Networking service.
|
||||
package lbaas
|
||||
123
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/requests.go
generated
vendored
Normal file
123
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/requests.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
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
|
||||
}
|
||||
243
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/requests_test.go
generated
vendored
Normal file
243
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
package members
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestURLs(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/lb/members", rootURL(fake.ServiceClient()))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/members", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"members":[
|
||||
{
|
||||
"status":"ACTIVE",
|
||||
"weight":1,
|
||||
"admin_state_up":true,
|
||||
"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
|
||||
"pool_id":"72741b06-df4d-4715-b142-276b6bce75ab",
|
||||
"address":"10.0.0.4",
|
||||
"protocol_port":80,
|
||||
"id":"701b531b-111a-4f21-ad85-4795b7b12af6"
|
||||
},
|
||||
{
|
||||
"status":"ACTIVE",
|
||||
"weight":1,
|
||||
"admin_state_up":true,
|
||||
"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
|
||||
"pool_id":"72741b06-df4d-4715-b142-276b6bce75ab",
|
||||
"address":"10.0.0.3",
|
||||
"protocol_port":80,
|
||||
"id":"beb53b4d-230b-4abd-8118-575b8fa006ef"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractMembers(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract members: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []Member{
|
||||
Member{
|
||||
Status: "ACTIVE",
|
||||
Weight: 1,
|
||||
AdminStateUp: true,
|
||||
TenantID: "83657cfcdfe44cd5920adaf26c48ceea",
|
||||
PoolID: "72741b06-df4d-4715-b142-276b6bce75ab",
|
||||
Address: "10.0.0.4",
|
||||
ProtocolPort: 80,
|
||||
ID: "701b531b-111a-4f21-ad85-4795b7b12af6",
|
||||
},
|
||||
Member{
|
||||
Status: "ACTIVE",
|
||||
Weight: 1,
|
||||
AdminStateUp: true,
|
||||
TenantID: "83657cfcdfe44cd5920adaf26c48ceea",
|
||||
PoolID: "72741b06-df4d-4715-b142-276b6bce75ab",
|
||||
Address: "10.0.0.3",
|
||||
ProtocolPort: 80,
|
||||
ID: "beb53b4d-230b-4abd-8118-575b8fa006ef",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/members", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"member": {
|
||||
"tenant_id": "453105b9-1754-413f-aab1-55f1af620750",
|
||||
"pool_id": "foo",
|
||||
"address": "192.0.2.14",
|
||||
"protocol_port":8080
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"member": {
|
||||
"id": "975592ca-e308-48ad-8298-731935ee9f45",
|
||||
"address": "192.0.2.14",
|
||||
"protocol_port": 8080,
|
||||
"tenant_id": "453105b9-1754-413f-aab1-55f1af620750",
|
||||
"admin_state_up":true,
|
||||
"weight": 1,
|
||||
"status": "DOWN"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := CreateOpts{
|
||||
TenantID: "453105b9-1754-413f-aab1-55f1af620750",
|
||||
Address: "192.0.2.14",
|
||||
ProtocolPort: 8080,
|
||||
PoolID: "foo",
|
||||
}
|
||||
_, err := Create(fake.ServiceClient(), options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/members/975592ca-e308-48ad-8298-731935ee9f45", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"member":{
|
||||
"id":"975592ca-e308-48ad-8298-731935ee9f45",
|
||||
"address":"192.0.2.14",
|
||||
"protocol_port":8080,
|
||||
"tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
|
||||
"admin_state_up":true,
|
||||
"weight":1,
|
||||
"status":"DOWN"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
m, err := Get(fake.ServiceClient(), "975592ca-e308-48ad-8298-731935ee9f45").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "975592ca-e308-48ad-8298-731935ee9f45", m.ID)
|
||||
th.AssertEquals(t, "192.0.2.14", m.Address)
|
||||
th.AssertEquals(t, 8080, m.ProtocolPort)
|
||||
th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", m.TenantID)
|
||||
th.AssertEquals(t, true, m.AdminStateUp)
|
||||
th.AssertEquals(t, 1, m.Weight)
|
||||
th.AssertEquals(t, "DOWN", m.Status)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/members/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"member":{
|
||||
"admin_state_up":false
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"member":{
|
||||
"status":"PENDING_UPDATE",
|
||||
"protocol_port":8080,
|
||||
"weight":1,
|
||||
"admin_state_up":false,
|
||||
"tenant_id":"4fd44f30292945e481c7b8a0c8908869",
|
||||
"pool_id":"7803631d-f181-4500-b3a2-1b68ba2a75fd",
|
||||
"address":"10.0.0.5",
|
||||
"status_description":null,
|
||||
"id":"48a471ea-64f1-4eb6-9be7-dae6bbe40a0f"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := UpdateOpts{AdminStateUp: false}
|
||||
|
||||
_, err := Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/members/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
122
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/results.go
generated
vendored
Normal file
122
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/results.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
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
|
||||
}
|
||||
16
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/urls.go
generated
vendored
Normal file
16
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/urls.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
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)
|
||||
}
|
||||
265
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/requests.go
generated
vendored
Normal file
265
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/requests.go
generated
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
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
|
||||
}
|
||||
312
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go
generated
vendored
Normal file
312
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
package monitors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestURLs(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/lb/health_monitors", rootURL(fake.ServiceClient()))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/health_monitors", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"health_monitors":[
|
||||
{
|
||||
"admin_state_up":true,
|
||||
"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
|
||||
"delay":10,
|
||||
"max_retries":1,
|
||||
"timeout":1,
|
||||
"type":"PING",
|
||||
"id":"466c8345-28d8-4f84-a246-e04380b0461d"
|
||||
},
|
||||
{
|
||||
"admin_state_up":true,
|
||||
"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
|
||||
"delay":5,
|
||||
"expected_codes":"200",
|
||||
"max_retries":2,
|
||||
"http_method":"GET",
|
||||
"timeout":2,
|
||||
"url_path":"/",
|
||||
"type":"HTTP",
|
||||
"id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractMonitors(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract monitors: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []Monitor{
|
||||
Monitor{
|
||||
AdminStateUp: true,
|
||||
TenantID: "83657cfcdfe44cd5920adaf26c48ceea",
|
||||
Delay: 10,
|
||||
MaxRetries: 1,
|
||||
Timeout: 1,
|
||||
Type: "PING",
|
||||
ID: "466c8345-28d8-4f84-a246-e04380b0461d",
|
||||
},
|
||||
Monitor{
|
||||
AdminStateUp: true,
|
||||
TenantID: "83657cfcdfe44cd5920adaf26c48ceea",
|
||||
Delay: 5,
|
||||
ExpectedCodes: "200",
|
||||
MaxRetries: 2,
|
||||
Timeout: 2,
|
||||
URLPath: "/",
|
||||
Type: "HTTP",
|
||||
HTTPMethod: "GET",
|
||||
ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) {
|
||||
_, err := Create(fake.ServiceClient(), CreateOpts{
|
||||
Type: "HTTP",
|
||||
Delay: 1,
|
||||
Timeout: 10,
|
||||
MaxRetries: 5,
|
||||
URLPath: "/check",
|
||||
ExpectedCodes: "200-299",
|
||||
}).Extract()
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
|
||||
_, err = Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", UpdateOpts{
|
||||
Delay: 1,
|
||||
Timeout: 10,
|
||||
}).Extract()
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/health_monitors", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"health_monitor":{
|
||||
"type":"HTTP",
|
||||
"tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
|
||||
"delay":20,
|
||||
"timeout":10,
|
||||
"max_retries":5,
|
||||
"url_path":"/check",
|
||||
"expected_codes":"200-299"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"health_monitor":{
|
||||
"id":"f3eeab00-8367-4524-b662-55e64d4cacb5",
|
||||
"tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
|
||||
"type":"HTTP",
|
||||
"delay":20,
|
||||
"timeout":10,
|
||||
"max_retries":5,
|
||||
"http_method":"GET",
|
||||
"url_path":"/check",
|
||||
"expected_codes":"200-299",
|
||||
"admin_state_up":true,
|
||||
"status":"ACTIVE"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
_, err := Create(fake.ServiceClient(), CreateOpts{
|
||||
Type: "HTTP",
|
||||
TenantID: "453105b9-1754-413f-aab1-55f1af620750",
|
||||
Delay: 20,
|
||||
Timeout: 10,
|
||||
MaxRetries: 5,
|
||||
URLPath: "/check",
|
||||
ExpectedCodes: "200-299",
|
||||
}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestRequiredCreateOpts(t *testing.T) {
|
||||
res := Create(fake.ServiceClient(), CreateOpts{})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
res = Create(fake.ServiceClient(), CreateOpts{Type: TypeHTTP})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/health_monitors/f3eeab00-8367-4524-b662-55e64d4cacb5", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"health_monitor":{
|
||||
"id":"f3eeab00-8367-4524-b662-55e64d4cacb5",
|
||||
"tenant_id":"453105b9-1754-413f-aab1-55f1af620750",
|
||||
"type":"HTTP",
|
||||
"delay":20,
|
||||
"timeout":10,
|
||||
"max_retries":5,
|
||||
"http_method":"GET",
|
||||
"url_path":"/check",
|
||||
"expected_codes":"200-299",
|
||||
"admin_state_up":true,
|
||||
"status":"ACTIVE"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
hm, err := Get(fake.ServiceClient(), "f3eeab00-8367-4524-b662-55e64d4cacb5").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "f3eeab00-8367-4524-b662-55e64d4cacb5", hm.ID)
|
||||
th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", hm.TenantID)
|
||||
th.AssertEquals(t, "HTTP", hm.Type)
|
||||
th.AssertEquals(t, 20, hm.Delay)
|
||||
th.AssertEquals(t, 10, hm.Timeout)
|
||||
th.AssertEquals(t, 5, hm.MaxRetries)
|
||||
th.AssertEquals(t, "GET", hm.HTTPMethod)
|
||||
th.AssertEquals(t, "/check", hm.URLPath)
|
||||
th.AssertEquals(t, "200-299", hm.ExpectedCodes)
|
||||
th.AssertEquals(t, true, hm.AdminStateUp)
|
||||
th.AssertEquals(t, "ACTIVE", hm.Status)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"health_monitor":{
|
||||
"delay": 3,
|
||||
"timeout": 20,
|
||||
"max_retries": 10,
|
||||
"url_path": "/another_check",
|
||||
"expected_codes": "301"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"health_monitor": {
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"delay": 3,
|
||||
"max_retries": 10,
|
||||
"http_method": "GET",
|
||||
"timeout": 20,
|
||||
"pools": [
|
||||
{
|
||||
"status": "PENDING_CREATE",
|
||||
"status_description": null,
|
||||
"pool_id": "6e55751f-6ad4-4e53-b8d4-02e442cd21df"
|
||||
}
|
||||
],
|
||||
"type": "PING",
|
||||
"id": "b05e44b5-81f9-4551-b474-711a722698f7"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
_, err := Update(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7", UpdateOpts{
|
||||
Delay: 3,
|
||||
Timeout: 20,
|
||||
MaxRetries: 10,
|
||||
URLPath: "/another_check",
|
||||
ExpectedCodes: "301",
|
||||
}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
147
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/results.go
generated
vendored
Normal file
147
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/results.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
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
|
||||
|
||||
// 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
|
||||
}
|
||||
16
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/urls.go
generated
vendored
Normal file
16
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/urls.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
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)
|
||||
}
|
||||
181
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/requests.go
generated
vendored
Normal file
181
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/requests.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
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
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
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,
|
||||
}}
|
||||
|
||||
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
|
||||
}
|
||||
317
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/requests_test.go
generated
vendored
Normal file
317
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,317 @@
|
||||
package pools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestURLs(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/lb/pools", rootURL(fake.ServiceClient()))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/pools", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"pools":[
|
||||
{
|
||||
"status":"ACTIVE",
|
||||
"lb_method":"ROUND_ROBIN",
|
||||
"protocol":"HTTP",
|
||||
"description":"",
|
||||
"health_monitors":[
|
||||
"466c8345-28d8-4f84-a246-e04380b0461d",
|
||||
"5d4b5228-33b0-4e60-b225-9b727c1a20e7"
|
||||
],
|
||||
"members":[
|
||||
"701b531b-111a-4f21-ad85-4795b7b12af6",
|
||||
"beb53b4d-230b-4abd-8118-575b8fa006ef"
|
||||
],
|
||||
"status_description": null,
|
||||
"id":"72741b06-df4d-4715-b142-276b6bce75ab",
|
||||
"vip_id":"4ec89087-d057-4e2c-911f-60a3b47ee304",
|
||||
"name":"app_pool",
|
||||
"admin_state_up":true,
|
||||
"subnet_id":"8032909d-47a1-4715-90af-5153ffe39861",
|
||||
"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
|
||||
"health_monitors_status": [],
|
||||
"provider": "haproxy"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractPools(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract pools: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []Pool{
|
||||
Pool{
|
||||
Status: "ACTIVE",
|
||||
LBMethod: "ROUND_ROBIN",
|
||||
Protocol: "HTTP",
|
||||
Description: "",
|
||||
MonitorIDs: []string{
|
||||
"466c8345-28d8-4f84-a246-e04380b0461d",
|
||||
"5d4b5228-33b0-4e60-b225-9b727c1a20e7",
|
||||
},
|
||||
SubnetID: "8032909d-47a1-4715-90af-5153ffe39861",
|
||||
TenantID: "83657cfcdfe44cd5920adaf26c48ceea",
|
||||
AdminStateUp: true,
|
||||
Name: "app_pool",
|
||||
MemberIDs: []string{
|
||||
"701b531b-111a-4f21-ad85-4795b7b12af6",
|
||||
"beb53b4d-230b-4abd-8118-575b8fa006ef",
|
||||
},
|
||||
ID: "72741b06-df4d-4715-b142-276b6bce75ab",
|
||||
VIPID: "4ec89087-d057-4e2c-911f-60a3b47ee304",
|
||||
Provider: "haproxy",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/pools", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"pool": {
|
||||
"lb_method": "ROUND_ROBIN",
|
||||
"protocol": "HTTP",
|
||||
"name": "Example pool",
|
||||
"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
|
||||
"tenant_id": "2ffc6e22aae24e4795f87155d24c896f"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"pool": {
|
||||
"status": "PENDING_CREATE",
|
||||
"lb_method": "ROUND_ROBIN",
|
||||
"protocol": "HTTP",
|
||||
"description": "",
|
||||
"health_monitors": [],
|
||||
"members": [],
|
||||
"status_description": null,
|
||||
"id": "69055154-f603-4a28-8951-7cc2d9e54a9a",
|
||||
"vip_id": null,
|
||||
"name": "Example pool",
|
||||
"admin_state_up": true,
|
||||
"subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9",
|
||||
"tenant_id": "2ffc6e22aae24e4795f87155d24c896f",
|
||||
"health_monitors_status": []
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := CreateOpts{
|
||||
LBMethod: LBMethodRoundRobin,
|
||||
Protocol: "HTTP",
|
||||
Name: "Example pool",
|
||||
SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9",
|
||||
TenantID: "2ffc6e22aae24e4795f87155d24c896f",
|
||||
}
|
||||
p, err := Create(fake.ServiceClient(), options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "PENDING_CREATE", p.Status)
|
||||
th.AssertEquals(t, "ROUND_ROBIN", p.LBMethod)
|
||||
th.AssertEquals(t, "HTTP", p.Protocol)
|
||||
th.AssertEquals(t, "", p.Description)
|
||||
th.AssertDeepEquals(t, []string{}, p.MonitorIDs)
|
||||
th.AssertDeepEquals(t, []string{}, p.MemberIDs)
|
||||
th.AssertEquals(t, "69055154-f603-4a28-8951-7cc2d9e54a9a", p.ID)
|
||||
th.AssertEquals(t, "Example pool", p.Name)
|
||||
th.AssertEquals(t, "1981f108-3c48-48d2-b908-30f7d28532c9", p.SubnetID)
|
||||
th.AssertEquals(t, "2ffc6e22aae24e4795f87155d24c896f", p.TenantID)
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"pool":{
|
||||
"id":"332abe93-f488-41ba-870b-2ac66be7f853",
|
||||
"tenant_id":"19eaa775-cf5d-49bc-902e-2f85f668d995",
|
||||
"name":"Example pool",
|
||||
"description":"",
|
||||
"protocol":"tcp",
|
||||
"lb_algorithm":"ROUND_ROBIN",
|
||||
"session_persistence":{
|
||||
},
|
||||
"healthmonitor_id":null,
|
||||
"members":[
|
||||
],
|
||||
"admin_state_up":true,
|
||||
"status":"ACTIVE"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
n, err := Get(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, n.ID, "332abe93-f488-41ba-870b-2ac66be7f853")
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"pool":{
|
||||
"name":"SuperPool",
|
||||
"lb_method": "LEAST_CONNECTIONS"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"pool":{
|
||||
"status":"PENDING_UPDATE",
|
||||
"lb_method":"LEAST_CONNECTIONS",
|
||||
"protocol":"TCP",
|
||||
"description":"",
|
||||
"health_monitors":[
|
||||
|
||||
],
|
||||
"subnet_id":"8032909d-47a1-4715-90af-5153ffe39861",
|
||||
"tenant_id":"83657cfcdfe44cd5920adaf26c48ceea",
|
||||
"admin_state_up":true,
|
||||
"name":"SuperPool",
|
||||
"members":[
|
||||
|
||||
],
|
||||
"id":"61b1f87a-7a21-4ad3-9dda-7f81d249944f",
|
||||
"vip_id":null
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := UpdateOpts{Name: "SuperPool", LBMethod: LBMethodLeastConnections}
|
||||
|
||||
n, err := Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "SuperPool", n.Name)
|
||||
th.AssertDeepEquals(t, "LEAST_CONNECTIONS", n.LBMethod)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
|
||||
func TestAssociateHealthMonitor(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853/health_monitors", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"health_monitor":{
|
||||
"id":"b624decf-d5d3-4c66-9a3d-f047e7786181"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
})
|
||||
|
||||
_, err := AssociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestDisassociateHealthMonitor(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853/health_monitors/b624decf-d5d3-4c66-9a3d-f047e7786181", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := DisassociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
146
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/results.go
generated
vendored
Normal file
146
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/results.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
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
|
||||
}
|
||||
25
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/urls.go
generated
vendored
Normal file
25
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/urls.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
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)
|
||||
}
|
||||
256
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/requests.go
generated
vendored
Normal file
256
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/requests.go
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
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
|
||||
}
|
||||
336
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/requests_test.go
generated
vendored
Normal file
336
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,336 @@
|
||||
package vips
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestURLs(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/lb/vips", rootURL(fake.ServiceClient()))
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/lb/vips/foo", resourceURL(fake.ServiceClient(), "foo"))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/vips", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"vips":[
|
||||
{
|
||||
"id": "db902c0c-d5ff-4753-b465-668ad9656918",
|
||||
"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
|
||||
"name": "web_vip",
|
||||
"description": "lb config for the web tier",
|
||||
"subnet_id": "96a4386a-f8c3-42ed-afce-d7954eee77b3",
|
||||
"address" : "10.30.176.47",
|
||||
"port_id" : "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
|
||||
"protocol": "HTTP",
|
||||
"protocol_port": 80,
|
||||
"pool_id" : "cfc6589d-f949-4c66-99d2-c2da56ef3764",
|
||||
"admin_state_up": true,
|
||||
"status": "ACTIVE"
|
||||
},
|
||||
{
|
||||
"id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
|
||||
"tenant_id": "310df60f-2a10-4ee5-9554-98393092194c",
|
||||
"name": "db_vip",
|
||||
"description": "lb config for the db tier",
|
||||
"subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
|
||||
"address" : "10.30.176.48",
|
||||
"port_id" : "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
|
||||
"protocol": "TCP",
|
||||
"protocol_port": 3306,
|
||||
"pool_id" : "41efe233-7591-43c5-9cf7-923964759f9e",
|
||||
"session_persistence" : {"type" : "SOURCE_IP"},
|
||||
"connection_limit" : 2000,
|
||||
"admin_state_up": true,
|
||||
"status": "INACTIVE"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractVIPs(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract LBs: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []VirtualIP{
|
||||
VirtualIP{
|
||||
ID: "db902c0c-d5ff-4753-b465-668ad9656918",
|
||||
TenantID: "310df60f-2a10-4ee5-9554-98393092194c",
|
||||
Name: "web_vip",
|
||||
Description: "lb config for the web tier",
|
||||
SubnetID: "96a4386a-f8c3-42ed-afce-d7954eee77b3",
|
||||
Address: "10.30.176.47",
|
||||
PortID: "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
|
||||
Protocol: "HTTP",
|
||||
ProtocolPort: 80,
|
||||
PoolID: "cfc6589d-f949-4c66-99d2-c2da56ef3764",
|
||||
Persistence: SessionPersistence{},
|
||||
ConnLimit: 0,
|
||||
AdminStateUp: true,
|
||||
Status: "ACTIVE",
|
||||
},
|
||||
VirtualIP{
|
||||
ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab",
|
||||
TenantID: "310df60f-2a10-4ee5-9554-98393092194c",
|
||||
Name: "db_vip",
|
||||
Description: "lb config for the db tier",
|
||||
SubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
|
||||
Address: "10.30.176.48",
|
||||
PortID: "cd1f7a47-4fa6-449c-9ee7-632838aedfea",
|
||||
Protocol: "TCP",
|
||||
ProtocolPort: 3306,
|
||||
PoolID: "41efe233-7591-43c5-9cf7-923964759f9e",
|
||||
Persistence: SessionPersistence{Type: "SOURCE_IP"},
|
||||
ConnLimit: 2000,
|
||||
AdminStateUp: true,
|
||||
Status: "INACTIVE",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/vips", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"vip": {
|
||||
"protocol": "HTTP",
|
||||
"name": "NewVip",
|
||||
"admin_state_up": true,
|
||||
"subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
|
||||
"pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
|
||||
"protocol_port": 80,
|
||||
"session_persistence": {"type": "SOURCE_IP"}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"vip": {
|
||||
"status": "PENDING_CREATE",
|
||||
"protocol": "HTTP",
|
||||
"description": "",
|
||||
"admin_state_up": true,
|
||||
"subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
|
||||
"tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
|
||||
"connection_limit": -1,
|
||||
"pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
|
||||
"address": "10.0.0.11",
|
||||
"protocol_port": 80,
|
||||
"port_id": "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5",
|
||||
"id": "c987d2be-9a3c-4ac9-a046-e8716b1350e2",
|
||||
"name": "NewVip"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
opts := CreateOpts{
|
||||
Protocol: "HTTP",
|
||||
Name: "NewVip",
|
||||
AdminStateUp: Up,
|
||||
SubnetID: "8032909d-47a1-4715-90af-5153ffe39861",
|
||||
PoolID: "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
|
||||
ProtocolPort: 80,
|
||||
Persistence: &SessionPersistence{Type: "SOURCE_IP"},
|
||||
}
|
||||
|
||||
r, err := Create(fake.ServiceClient(), opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "PENDING_CREATE", r.Status)
|
||||
th.AssertEquals(t, "HTTP", r.Protocol)
|
||||
th.AssertEquals(t, "", r.Description)
|
||||
th.AssertEquals(t, true, r.AdminStateUp)
|
||||
th.AssertEquals(t, "8032909d-47a1-4715-90af-5153ffe39861", r.SubnetID)
|
||||
th.AssertEquals(t, "83657cfcdfe44cd5920adaf26c48ceea", r.TenantID)
|
||||
th.AssertEquals(t, -1, r.ConnLimit)
|
||||
th.AssertEquals(t, "61b1f87a-7a21-4ad3-9dda-7f81d249944f", r.PoolID)
|
||||
th.AssertEquals(t, "10.0.0.11", r.Address)
|
||||
th.AssertEquals(t, 80, r.ProtocolPort)
|
||||
th.AssertEquals(t, "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5", r.PortID)
|
||||
th.AssertEquals(t, "c987d2be-9a3c-4ac9-a046-e8716b1350e2", r.ID)
|
||||
th.AssertEquals(t, "NewVip", r.Name)
|
||||
}
|
||||
|
||||
func TestRequiredCreateOpts(t *testing.T) {
|
||||
res := Create(fake.ServiceClient(), CreateOpts{})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
res = Create(fake.ServiceClient(), CreateOpts{Name: "foo"})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
res = Create(fake.ServiceClient(), CreateOpts{Name: "foo", SubnetID: "bar"})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
res = Create(fake.ServiceClient(), CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar"})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
res = Create(fake.ServiceClient(), CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar", ProtocolPort: 80})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"vip": {
|
||||
"status": "ACTIVE",
|
||||
"protocol": "HTTP",
|
||||
"description": "",
|
||||
"admin_state_up": true,
|
||||
"subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
|
||||
"tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
|
||||
"connection_limit": 1000,
|
||||
"pool_id": "72741b06-df4d-4715-b142-276b6bce75ab",
|
||||
"session_persistence": {
|
||||
"cookie_name": "MyAppCookie",
|
||||
"type": "APP_COOKIE"
|
||||
},
|
||||
"address": "10.0.0.10",
|
||||
"protocol_port": 80,
|
||||
"port_id": "b5a743d6-056b-468b-862d-fb13a9aa694e",
|
||||
"id": "4ec89087-d057-4e2c-911f-60a3b47ee304",
|
||||
"name": "my-vip"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
vip, err := Get(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "ACTIVE", vip.Status)
|
||||
th.AssertEquals(t, "HTTP", vip.Protocol)
|
||||
th.AssertEquals(t, "", vip.Description)
|
||||
th.AssertEquals(t, true, vip.AdminStateUp)
|
||||
th.AssertEquals(t, 1000, vip.ConnLimit)
|
||||
th.AssertEquals(t, SessionPersistence{Type: "APP_COOKIE", CookieName: "MyAppCookie"}, vip.Persistence)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"vip": {
|
||||
"connection_limit": 1000,
|
||||
"session_persistence": {"type": "SOURCE_IP"}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"vip": {
|
||||
"status": "PENDING_UPDATE",
|
||||
"protocol": "HTTP",
|
||||
"description": "",
|
||||
"admin_state_up": true,
|
||||
"subnet_id": "8032909d-47a1-4715-90af-5153ffe39861",
|
||||
"tenant_id": "83657cfcdfe44cd5920adaf26c48ceea",
|
||||
"connection_limit": 1000,
|
||||
"pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f",
|
||||
"address": "10.0.0.11",
|
||||
"protocol_port": 80,
|
||||
"port_id": "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5",
|
||||
"id": "c987d2be-9a3c-4ac9-a046-e8716b1350e2",
|
||||
"name": "NewVip"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
i1000 := 1000
|
||||
options := UpdateOpts{
|
||||
ConnLimit: &i1000,
|
||||
Persistence: &SessionPersistence{Type: "SOURCE_IP"},
|
||||
}
|
||||
vip, err := Update(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304", options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "PENDING_UPDATE", vip.Status)
|
||||
th.AssertEquals(t, 1000, vip.ConnLimit)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
166
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/results.go
generated
vendored
Normal file
166
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/results.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
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
|
||||
}
|
||||
16
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/urls.go
generated
vendored
Normal file
16
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/urls.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
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)
|
||||
}
|
||||
21
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/doc.go
generated
vendored
Normal file
21
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/doc.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Package provider gives access to the provider Neutron plugin, allowing
|
||||
// network extended attributes. The provider extended attributes for networks
|
||||
// enable administrative users to specify how network objects map to the
|
||||
// underlying networking infrastructure. These extended attributes also appear
|
||||
// when administrative users query networks.
|
||||
//
|
||||
// For more information about extended attributes, see the NetworkExtAttrs
|
||||
// struct. The actual semantics of these attributes depend on the technology
|
||||
// back end of the particular plug-in. See the plug-in documentation and the
|
||||
// OpenStack Cloud Administrator Guide to understand which values should be
|
||||
// specific for each of these attributes when OpenStack Networking is deployed
|
||||
// with a particular plug-in. The examples shown in this chapter refer to the
|
||||
// Open vSwitch plug-in.
|
||||
//
|
||||
// The default policy settings enable only users with administrative rights to
|
||||
// specify these parameters in requests and to see their values in responses. By
|
||||
// default, the provider network extension attributes are completely hidden from
|
||||
// regular tenants. As a rule of thumb, if these attributes are not visible in a
|
||||
// GET /networks/<network-id> operation, this implies the user submitting the
|
||||
// request is not authorized to view or manipulate provider network attributes.
|
||||
package provider
|
||||
124
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results.go
generated
vendored
Normal file
124
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"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
|
||||
)
|
||||
|
||||
// NetworkExtAttrs represents an extended form of a Network with additional fields.
|
||||
type NetworkExtAttrs struct {
|
||||
// UUID for the network
|
||||
ID string `mapstructure:"id" json:"id"`
|
||||
|
||||
// Human-readable name for the network. Might not be unique.
|
||||
Name string `mapstructure:"name" json:"name"`
|
||||
|
||||
// The administrative state of network. If false (down), the network 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"`
|
||||
|
||||
// Subnets associated with this network.
|
||||
Subnets []string `mapstructure:"subnets" json:"subnets"`
|
||||
|
||||
// Owner of network. Only admin users can specify a tenant_id other than its own.
|
||||
TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
|
||||
|
||||
// Specifies whether the network resource can be accessed by any tenant or not.
|
||||
Shared bool `mapstructure:"shared" json:"shared"`
|
||||
|
||||
// Specifies the nature of the physical network mapped to this network
|
||||
// resource. Examples are flat, vlan, or gre.
|
||||
NetworkType string `json:"provider:network_type" mapstructure:"provider:network_type"`
|
||||
|
||||
// Identifies the physical network on top of which this network object is
|
||||
// being implemented. The OpenStack Networking API does not expose any facility
|
||||
// for retrieving the list of available physical networks. As an example, in
|
||||
// the Open vSwitch plug-in this is a symbolic name which is then mapped to
|
||||
// specific bridges on each compute host through the Open vSwitch plug-in
|
||||
// configuration file.
|
||||
PhysicalNetwork string `json:"provider:physical_network" mapstructure:"provider:physical_network"`
|
||||
|
||||
// Identifies an isolated segment on the physical network; the nature of the
|
||||
// segment depends on the segmentation model defined by network_type. For
|
||||
// instance, if network_type is vlan, then this is a vlan identifier;
|
||||
// otherwise, if network_type is gre, then this will be a gre key.
|
||||
SegmentationID string `json:"provider:segmentation_id" mapstructure:"provider:segmentation_id"`
|
||||
}
|
||||
|
||||
// ExtractGet decorates a GetResult struct returned from a networks.Get()
|
||||
// function with extended attributes.
|
||||
func ExtractGet(r networks.GetResult) (*NetworkExtAttrs, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Network *NetworkExtAttrs `json:"network"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(r.Body, &res)
|
||||
|
||||
return res.Network, err
|
||||
}
|
||||
|
||||
// ExtractCreate decorates a CreateResult struct returned from a networks.Create()
|
||||
// function with extended attributes.
|
||||
func ExtractCreate(r networks.CreateResult) (*NetworkExtAttrs, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Network *NetworkExtAttrs `json:"network"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(r.Body, &res)
|
||||
|
||||
return res.Network, err
|
||||
}
|
||||
|
||||
// ExtractUpdate decorates a UpdateResult struct returned from a
|
||||
// networks.Update() function with extended attributes.
|
||||
func ExtractUpdate(r networks.UpdateResult) (*NetworkExtAttrs, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Network *NetworkExtAttrs `json:"network"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(r.Body, &res)
|
||||
|
||||
return res.Network, err
|
||||
}
|
||||
|
||||
// ExtractList accepts a Page struct, specifically a NetworkPage struct, and
|
||||
// extracts the elements into a slice of NetworkExtAttrs structs. In other
|
||||
// words, a generic collection is mapped into a relevant slice.
|
||||
func ExtractList(page pagination.Page) ([]NetworkExtAttrs, error) {
|
||||
var resp struct {
|
||||
Networks []NetworkExtAttrs `mapstructure:"networks" json:"networks"`
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(page.(networks.NetworkPage).Body, &resp)
|
||||
|
||||
return resp.Networks, err
|
||||
}
|
||||
253
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results_test.go
generated
vendored
Normal file
253
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results_test.go
generated
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"networks": [
|
||||
{
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
|
||||
],
|
||||
"name": "private-network",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"shared": true,
|
||||
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
"provider:segmentation_id": null,
|
||||
"provider:physical_network": null,
|
||||
"provider:network_type": "local"
|
||||
},
|
||||
{
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"08eae331-0402-425a-923c-34f7cfe39c1b"
|
||||
],
|
||||
"name": "private",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
|
||||
"shared": true,
|
||||
"id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
|
||||
"provider:segmentation_id": 1234567890,
|
||||
"provider:physical_network": null,
|
||||
"provider:network_type": "local"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
networks.List(fake.ServiceClient(), networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractList(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract networks: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []NetworkExtAttrs{
|
||||
NetworkExtAttrs{
|
||||
Status: "ACTIVE",
|
||||
Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"},
|
||||
Name: "private-network",
|
||||
AdminStateUp: true,
|
||||
TenantID: "4fd44f30292945e481c7b8a0c8908869",
|
||||
Shared: true,
|
||||
ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
NetworkType: "local",
|
||||
PhysicalNetwork: "",
|
||||
SegmentationID: "",
|
||||
},
|
||||
NetworkExtAttrs{
|
||||
Status: "ACTIVE",
|
||||
Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"},
|
||||
Name: "private",
|
||||
AdminStateUp: true,
|
||||
TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e",
|
||||
Shared: true,
|
||||
ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
|
||||
NetworkType: "local",
|
||||
PhysicalNetwork: "",
|
||||
SegmentationID: "1234567890",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"network": {
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
|
||||
],
|
||||
"name": "private-network",
|
||||
"provider:physical_network": null,
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"provider:network_type": "local",
|
||||
"shared": true,
|
||||
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
"provider:segmentation_id": null
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
res := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
|
||||
n, err := ExtractGet(res)
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "", n.PhysicalNetwork)
|
||||
th.AssertEquals(t, "local", n.NetworkType)
|
||||
th.AssertEquals(t, "", n.SegmentationID)
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"network": {
|
||||
"name": "sample_network",
|
||||
"admin_state_up": true
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"network": {
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
|
||||
],
|
||||
"name": "private-network",
|
||||
"provider:physical_network": null,
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"provider:network_type": "local",
|
||||
"shared": true,
|
||||
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
"provider:segmentation_id": null
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := networks.CreateOpts{Name: "sample_network", AdminStateUp: Up}
|
||||
res := networks.Create(fake.ServiceClient(), options)
|
||||
n, err := ExtractCreate(res)
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "", n.PhysicalNetwork)
|
||||
th.AssertEquals(t, "local", n.NetworkType)
|
||||
th.AssertEquals(t, "", n.SegmentationID)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"network": {
|
||||
"name": "new_network_name",
|
||||
"admin_state_up": false,
|
||||
"shared": true
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"network": {
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
|
||||
],
|
||||
"name": "private-network",
|
||||
"provider:physical_network": null,
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"provider:network_type": "local",
|
||||
"shared": true,
|
||||
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
"provider:segmentation_id": null
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
iTrue := true
|
||||
options := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: Down, Shared: &iTrue}
|
||||
res := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options)
|
||||
n, err := ExtractUpdate(res)
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "", n.PhysicalNetwork)
|
||||
th.AssertEquals(t, "local", n.NetworkType)
|
||||
th.AssertEquals(t, "", n.SegmentationID)
|
||||
}
|
||||
32
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/doc.go
generated
vendored
Normal file
32
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/doc.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// Package security contains functionality to work with security group and
|
||||
// security group rules Neutron resources.
|
||||
//
|
||||
// Security groups and security group rules allows administrators and tenants
|
||||
// the ability to specify the type of traffic and direction (ingress/egress)
|
||||
// that is allowed to pass through a port. A security group is a container for
|
||||
// security group rules.
|
||||
//
|
||||
// When a port is created in Networking it is associated with a security group.
|
||||
// If a security group is not specified the port is associated with a 'default'
|
||||
// security group. By default, this group drops all ingress traffic and allows
|
||||
// all egress. Rules can be added to this group in order to change the behaviour.
|
||||
//
|
||||
// The basic characteristics of Neutron Security Groups are:
|
||||
//
|
||||
// For ingress traffic (to an instance)
|
||||
// - Only traffic matched with security group rules are allowed.
|
||||
// - When there is no rule defined, all traffic is dropped.
|
||||
//
|
||||
// For egress traffic (from an instance)
|
||||
// - Only traffic matched with security group rules are allowed.
|
||||
// - When there is no rule defined, all egress traffic are dropped.
|
||||
// - When a new security group is created, rules to allow all egress traffic
|
||||
// is automatically added.
|
||||
//
|
||||
// "default security group" is defined for each tenant.
|
||||
// - For the default security group a rule which allows intercommunication
|
||||
// among hosts associated with the default security group is defined by default.
|
||||
// - As a result, all egress traffic and intercommunication in the default
|
||||
// group are allowed and all ingress from outside of the default group is
|
||||
// dropped by default (in the default security group).
|
||||
package security
|
||||
93
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go
generated
vendored
Normal file
93
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
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
|
||||
|
||||
// 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"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
type request struct {
|
||||
SecGroup secgroup `json:"security_group"`
|
||||
}
|
||||
|
||||
reqBody := request{SecGroup: secgroup{
|
||||
Name: opts.Name,
|
||||
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
|
||||
}
|
||||
213
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/requests_test.go
generated
vendored
Normal file
213
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestURLs(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/security-groups", rootURL(fake.ServiceClient()))
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/security-groups/foo", resourceURL(fake.ServiceClient(), "foo"))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/security-groups", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_groups": [
|
||||
{
|
||||
"description": "default",
|
||||
"id": "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
"name": "default",
|
||||
"security_group_rules": [],
|
||||
"tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractGroups(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract secgroups: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []SecGroup{
|
||||
SecGroup{
|
||||
Description: "default",
|
||||
ID: "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
Name: "default",
|
||||
Rules: []rules.SecGroupRule{},
|
||||
TenantID: "e4f50856753b4dc6afee5fa6b9b6c550",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/security-groups", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"security_group": {
|
||||
"name": "new-webservers",
|
||||
"description": "security group for webservers"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group": {
|
||||
"description": "security group for webservers",
|
||||
"id": "2076db17-a522-4506-91de-c6dd8e837028",
|
||||
"name": "new-webservers",
|
||||
"security_group_rules": [
|
||||
{
|
||||
"direction": "egress",
|
||||
"ethertype": "IPv4",
|
||||
"id": "38ce2d8e-e8f1-48bd-83c2-d33cb9f50c3d",
|
||||
"port_range_max": null,
|
||||
"port_range_min": null,
|
||||
"protocol": null,
|
||||
"remote_group_id": null,
|
||||
"remote_ip_prefix": null,
|
||||
"security_group_id": "2076db17-a522-4506-91de-c6dd8e837028",
|
||||
"tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
|
||||
},
|
||||
{
|
||||
"direction": "egress",
|
||||
"ethertype": "IPv6",
|
||||
"id": "565b9502-12de-4ffd-91e9-68885cff6ae1",
|
||||
"port_range_max": null,
|
||||
"port_range_min": null,
|
||||
"protocol": null,
|
||||
"remote_group_id": null,
|
||||
"remote_ip_prefix": null,
|
||||
"security_group_id": "2076db17-a522-4506-91de-c6dd8e837028",
|
||||
"tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
|
||||
}
|
||||
],
|
||||
"tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
opts := CreateOpts{Name: "new-webservers", Description: "security group for webservers"}
|
||||
_, err := Create(fake.ServiceClient(), opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/security-groups/85cc3048-abc3-43cc-89b3-377341426ac5", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group": {
|
||||
"description": "default",
|
||||
"id": "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
"name": "default",
|
||||
"security_group_rules": [
|
||||
{
|
||||
"direction": "egress",
|
||||
"ethertype": "IPv6",
|
||||
"id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
|
||||
"port_range_max": null,
|
||||
"port_range_min": null,
|
||||
"protocol": null,
|
||||
"remote_group_id": null,
|
||||
"remote_ip_prefix": null,
|
||||
"security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
"tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
|
||||
},
|
||||
{
|
||||
"direction": "egress",
|
||||
"ethertype": "IPv4",
|
||||
"id": "93aa42e5-80db-4581-9391-3a608bd0e448",
|
||||
"port_range_max": null,
|
||||
"port_range_min": null,
|
||||
"protocol": null,
|
||||
"remote_group_id": null,
|
||||
"remote_ip_prefix": null,
|
||||
"security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
"tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
|
||||
}
|
||||
],
|
||||
"tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
sg, err := Get(fake.ServiceClient(), "85cc3048-abc3-43cc-89b3-377341426ac5").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "default", sg.Description)
|
||||
th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sg.ID)
|
||||
th.AssertEquals(t, "default", sg.Name)
|
||||
th.AssertEquals(t, 2, len(sg.Rules))
|
||||
th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sg.TenantID)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/security-groups/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
108
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/results.go
generated
vendored
Normal file
108
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/results.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
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
|
||||
}
|
||||
13
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/urls.go
generated
vendored
Normal file
13
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/urls.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
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)
|
||||
}
|
||||
169
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go
generated
vendored
Normal file
169
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
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
|
||||
}
|
||||
|
||||
// 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.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"`
|
||||
}
|
||||
|
||||
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,
|
||||
}}
|
||||
|
||||
_, 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
|
||||
}
|
||||
243
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/requests_test.go
generated
vendored
Normal file
243
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestURLs(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/security-group-rules", rootURL(fake.ServiceClient()))
|
||||
th.AssertEquals(t, th.Endpoint()+"v2.0/security-group-rules/foo", resourceURL(fake.ServiceClient(), "foo"))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group_rules": [
|
||||
{
|
||||
"direction": "egress",
|
||||
"ethertype": "IPv6",
|
||||
"id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
|
||||
"port_range_max": null,
|
||||
"port_range_min": null,
|
||||
"protocol": null,
|
||||
"remote_group_id": null,
|
||||
"remote_ip_prefix": null,
|
||||
"security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
"tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
|
||||
},
|
||||
{
|
||||
"direction": "egress",
|
||||
"ethertype": "IPv4",
|
||||
"id": "93aa42e5-80db-4581-9391-3a608bd0e448",
|
||||
"port_range_max": null,
|
||||
"port_range_min": null,
|
||||
"protocol": null,
|
||||
"remote_group_id": null,
|
||||
"remote_ip_prefix": null,
|
||||
"security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
"tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractRules(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract secrules: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []SecGroupRule{
|
||||
SecGroupRule{
|
||||
Direction: "egress",
|
||||
EtherType: "IPv6",
|
||||
ID: "3c0e45ff-adaf-4124-b083-bf390e5482ff",
|
||||
PortRangeMax: 0,
|
||||
PortRangeMin: 0,
|
||||
Protocol: "",
|
||||
RemoteGroupID: "",
|
||||
RemoteIPPrefix: "",
|
||||
SecGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
TenantID: "e4f50856753b4dc6afee5fa6b9b6c550",
|
||||
},
|
||||
SecGroupRule{
|
||||
Direction: "egress",
|
||||
EtherType: "IPv4",
|
||||
ID: "93aa42e5-80db-4581-9391-3a608bd0e448",
|
||||
PortRangeMax: 0,
|
||||
PortRangeMin: 0,
|
||||
Protocol: "",
|
||||
RemoteGroupID: "",
|
||||
RemoteIPPrefix: "",
|
||||
SecGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
TenantID: "e4f50856753b4dc6afee5fa6b9b6c550",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"security_group_rule": {
|
||||
"direction": "ingress",
|
||||
"port_range_min": 80,
|
||||
"ethertype": "IPv4",
|
||||
"port_range_max": 80,
|
||||
"protocol": "tcp",
|
||||
"remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
"security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group_rule": {
|
||||
"direction": "ingress",
|
||||
"ethertype": "IPv4",
|
||||
"id": "2bc0accf-312e-429a-956e-e4407625eb62",
|
||||
"port_range_max": 80,
|
||||
"port_range_min": 80,
|
||||
"protocol": "tcp",
|
||||
"remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
"remote_ip_prefix": null,
|
||||
"security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a",
|
||||
"tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
opts := CreateOpts{
|
||||
Direction: "ingress",
|
||||
PortRangeMin: 80,
|
||||
EtherType: "IPv4",
|
||||
PortRangeMax: 80,
|
||||
Protocol: "tcp",
|
||||
RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a",
|
||||
}
|
||||
_, err := Create(fake.ServiceClient(), opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestRequiredCreateOpts(t *testing.T) {
|
||||
res := Create(fake.ServiceClient(), CreateOpts{Direction: "something"})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
res = Create(fake.ServiceClient(), CreateOpts{Direction: DirIngress, EtherType: "something"})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
res = Create(fake.ServiceClient(), CreateOpts{Direction: DirIngress, EtherType: Ether4})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
res = Create(fake.ServiceClient(), CreateOpts{Direction: DirIngress, EtherType: Ether4, SecGroupID: "something", Protocol: "foo"})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/security-group-rules/3c0e45ff-adaf-4124-b083-bf390e5482ff", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"security_group_rule": {
|
||||
"direction": "egress",
|
||||
"ethertype": "IPv6",
|
||||
"id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
|
||||
"port_range_max": null,
|
||||
"port_range_min": null,
|
||||
"protocol": null,
|
||||
"remote_group_id": null,
|
||||
"remote_ip_prefix": null,
|
||||
"security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
|
||||
"tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
sr, err := Get(fake.ServiceClient(), "3c0e45ff-adaf-4124-b083-bf390e5482ff").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, "egress", sr.Direction)
|
||||
th.AssertEquals(t, "IPv6", sr.EtherType)
|
||||
th.AssertEquals(t, "3c0e45ff-adaf-4124-b083-bf390e5482ff", sr.ID)
|
||||
th.AssertEquals(t, 0, sr.PortRangeMax)
|
||||
th.AssertEquals(t, 0, sr.PortRangeMin)
|
||||
th.AssertEquals(t, "", sr.Protocol)
|
||||
th.AssertEquals(t, "", sr.RemoteGroupID)
|
||||
th.AssertEquals(t, "", sr.RemoteIPPrefix)
|
||||
th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sr.SecGroupID)
|
||||
th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sr.TenantID)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/security-group-rules/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
133
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/results.go
generated
vendored
Normal file
133
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/results.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
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
|
||||
}
|
||||
13
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/urls.go
generated
vendored
Normal file
13
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/urls.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
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)
|
||||
}
|
||||
9
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/doc.go
generated
vendored
Normal file
9
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/doc.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Package networks contains functionality for working with Neutron network
|
||||
// resources. A network is an isolated virtual layer-2 broadcast domain that is
|
||||
// typically reserved for the tenant who created it (unless you configure the
|
||||
// network to be shared). Tenants can create multiple networks until the
|
||||
// thresholds per-tenant quota is reached.
|
||||
//
|
||||
// In the v2.0 Networking API, the network is the main entity. Ports and subnets
|
||||
// are always associated with a network.
|
||||
package networks
|
||||
1
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/errors.go
generated
vendored
Normal file
1
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/errors.go
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package networks
|
||||
191
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/requests.go
generated
vendored
Normal file
191
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/requests.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
package networks
|
||||
|
||||
import (
|
||||
"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
|
||||
)
|
||||
|
||||
type networkOpts struct {
|
||||
AdminStateUp *bool
|
||||
Name string
|
||||
Shared *bool
|
||||
TenantID string
|
||||
}
|
||||
|
||||
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||
// List request.
|
||||
type ListOptsBuilder interface {
|
||||
ToNetworkListQuery() (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 network 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"`
|
||||
Name string `q:"name"`
|
||||
AdminStateUp *bool `q:"admin_state_up"`
|
||||
TenantID string `q:"tenant_id"`
|
||||
Shared *bool `q:"shared"`
|
||||
ID string `q:"id"`
|
||||
Marker string `q:"marker"`
|
||||
Limit int `q:"limit"`
|
||||
SortKey string `q:"sort_key"`
|
||||
SortDir string `q:"sort_dir"`
|
||||
}
|
||||
|
||||
// ToNetworkListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToNetworkListQuery() (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
|
||||
// networks. It accepts a ListOpts struct, which allows you to filter and sort
|
||||
// the returned collection for greater efficiency.
|
||||
func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||
url := listURL(c)
|
||||
if opts != nil {
|
||||
query, err := opts.ToNetworkListQuery()
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
url += query
|
||||
}
|
||||
|
||||
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
|
||||
return NetworkPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
||||
// Get retrieves a specific network 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 {
|
||||
ToNetworkCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts is the common options struct used in this package's Create
|
||||
// operation.
|
||||
type CreateOpts networkOpts
|
||||
|
||||
// ToNetworkCreateMap casts a CreateOpts struct to a map.
|
||||
func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
|
||||
n := make(map[string]interface{})
|
||||
|
||||
if opts.AdminStateUp != nil {
|
||||
n["admin_state_up"] = &opts.AdminStateUp
|
||||
}
|
||||
if opts.Name != "" {
|
||||
n["name"] = opts.Name
|
||||
}
|
||||
if opts.Shared != nil {
|
||||
n["shared"] = &opts.Shared
|
||||
}
|
||||
if opts.TenantID != "" {
|
||||
n["tenant_id"] = opts.TenantID
|
||||
}
|
||||
|
||||
return map[string]interface{}{"network": n}, nil
|
||||
}
|
||||
|
||||
// Create accepts a CreateOpts struct and creates a new network using the values
|
||||
// provided. This operation does not actually require a request body, i.e. the
|
||||
// CreateOpts struct argument can be empty.
|
||||
//
|
||||
// The tenant ID that is contained in the URI is the tenant that creates the
|
||||
// network. An admin user, however, has the option of specifying another tenant
|
||||
// ID in the CreateOpts struct.
|
||||
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||
var res CreateResult
|
||||
|
||||
reqBody, err := opts.ToNetworkCreateMap()
|
||||
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 {
|
||||
ToNetworkUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateOpts is the common options struct used in this package's Update
|
||||
// operation.
|
||||
type UpdateOpts networkOpts
|
||||
|
||||
// ToNetworkUpdateMap casts a UpdateOpts struct to a map.
|
||||
func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) {
|
||||
n := make(map[string]interface{})
|
||||
|
||||
if opts.AdminStateUp != nil {
|
||||
n["admin_state_up"] = &opts.AdminStateUp
|
||||
}
|
||||
if opts.Name != "" {
|
||||
n["name"] = opts.Name
|
||||
}
|
||||
if opts.Shared != nil {
|
||||
n["shared"] = &opts.Shared
|
||||
}
|
||||
|
||||
return map[string]interface{}{"network": n}, nil
|
||||
}
|
||||
|
||||
// Update accepts a UpdateOpts struct and updates an existing network using the
|
||||
// values provided. For more information, see the Create function.
|
||||
func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuilder) UpdateResult {
|
||||
var res UpdateResult
|
||||
|
||||
reqBody, err := opts.ToNetworkUpdateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
// Send request to API
|
||||
_, res.Err = c.Put(updateURL(c, networkID), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200, 201},
|
||||
})
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Delete accepts a unique ID and deletes the network associated with it.
|
||||
func Delete(c *gophercloud.ServiceClient, networkID string) DeleteResult {
|
||||
var res DeleteResult
|
||||
_, res.Err = c.Delete(deleteURL(c, networkID), nil)
|
||||
return res
|
||||
}
|
||||
275
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/requests_test.go
generated
vendored
Normal file
275
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
package networks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"networks": [
|
||||
{
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
|
||||
],
|
||||
"name": "private-network",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"shared": true,
|
||||
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
|
||||
},
|
||||
{
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"08eae331-0402-425a-923c-34f7cfe39c1b"
|
||||
],
|
||||
"name": "private",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
|
||||
"shared": true,
|
||||
"id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
client := fake.ServiceClient()
|
||||
count := 0
|
||||
|
||||
List(client, ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractNetworks(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract networks: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
expected := []Network{
|
||||
Network{
|
||||
Status: "ACTIVE",
|
||||
Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"},
|
||||
Name: "private-network",
|
||||
AdminStateUp: true,
|
||||
TenantID: "4fd44f30292945e481c7b8a0c8908869",
|
||||
Shared: true,
|
||||
ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
},
|
||||
Network{
|
||||
Status: "ACTIVE",
|
||||
Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"},
|
||||
Name: "private",
|
||||
AdminStateUp: true,
|
||||
TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e",
|
||||
Shared: true,
|
||||
ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"network": {
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
|
||||
],
|
||||
"name": "private-network",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"shared": true,
|
||||
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
n, err := Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, n.Status, "ACTIVE")
|
||||
th.AssertDeepEquals(t, n.Subnets, []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"})
|
||||
th.AssertEquals(t, n.Name, "private-network")
|
||||
th.AssertEquals(t, n.AdminStateUp, true)
|
||||
th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869")
|
||||
th.AssertEquals(t, n.Shared, true)
|
||||
th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"network": {
|
||||
"name": "sample_network",
|
||||
"admin_state_up": true
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"network": {
|
||||
"status": "ACTIVE",
|
||||
"subnets": [],
|
||||
"name": "net1",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "9bacb3c5d39d41a79512987f338cf177",
|
||||
"shared": false,
|
||||
"id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
iTrue := true
|
||||
options := CreateOpts{Name: "sample_network", AdminStateUp: &iTrue}
|
||||
n, err := Create(fake.ServiceClient(), options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, n.Status, "ACTIVE")
|
||||
th.AssertDeepEquals(t, n.Subnets, []string{})
|
||||
th.AssertEquals(t, n.Name, "net1")
|
||||
th.AssertEquals(t, n.AdminStateUp, true)
|
||||
th.AssertEquals(t, n.TenantID, "9bacb3c5d39d41a79512987f338cf177")
|
||||
th.AssertEquals(t, n.Shared, false)
|
||||
th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
|
||||
}
|
||||
|
||||
func TestCreateWithOptionalFields(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"network": {
|
||||
"name": "sample_network",
|
||||
"admin_state_up": true,
|
||||
"shared": true,
|
||||
"tenant_id": "12345"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
})
|
||||
|
||||
iTrue := true
|
||||
options := CreateOpts{Name: "sample_network", AdminStateUp: &iTrue, Shared: &iTrue, TenantID: "12345"}
|
||||
_, err := Create(fake.ServiceClient(), options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"network": {
|
||||
"name": "new_network_name",
|
||||
"admin_state_up": false,
|
||||
"shared": true
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"network": {
|
||||
"status": "ACTIVE",
|
||||
"subnets": [],
|
||||
"name": "new_network_name",
|
||||
"admin_state_up": false,
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"shared": true,
|
||||
"id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
iTrue, iFalse := true, false
|
||||
options := UpdateOpts{Name: "new_network_name", AdminStateUp: &iFalse, Shared: &iTrue}
|
||||
n, err := Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, n.Name, "new_network_name")
|
||||
th.AssertEquals(t, n.AdminStateUp, false)
|
||||
th.AssertEquals(t, n.Shared, true)
|
||||
th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
116
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/results.go
generated
vendored
Normal file
116
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/results.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package networks
|
||||
|
||||
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 network resource.
|
||||
func (r commonResult) Extract() (*Network, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Network *Network `json:"network"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &res)
|
||||
|
||||
return res.Network, 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
|
||||
}
|
||||
|
||||
// Network represents, well, a network.
|
||||
type Network struct {
|
||||
// UUID for the network
|
||||
ID string `mapstructure:"id" json:"id"`
|
||||
|
||||
// Human-readable name for the network. Might not be unique.
|
||||
Name string `mapstructure:"name" json:"name"`
|
||||
|
||||
// The administrative state of network. If false (down), the network 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"`
|
||||
|
||||
// Subnets associated with this network.
|
||||
Subnets []string `mapstructure:"subnets" json:"subnets"`
|
||||
|
||||
// Owner of network. Only admin users can specify a tenant_id other than its own.
|
||||
TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
|
||||
|
||||
// Specifies whether the network resource can be accessed by any tenant or not.
|
||||
Shared bool `mapstructure:"shared" json:"shared"`
|
||||
}
|
||||
|
||||
// NetworkPage is the page returned by a pager when traversing over a
|
||||
// collection of networks.
|
||||
type NetworkPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// NextPageURL is invoked when a paginated collection of networks 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 NetworkPage) NextPageURL() (string, error) {
|
||||
type resp struct {
|
||||
Links []gophercloud.Link `mapstructure:"networks_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 NetworkPage) IsEmpty() (bool, error) {
|
||||
is, err := ExtractNetworks(p)
|
||||
if err != nil {
|
||||
return true, nil
|
||||
}
|
||||
return len(is) == 0, nil
|
||||
}
|
||||
|
||||
// ExtractNetworks accepts a Page struct, specifically a NetworkPage struct,
|
||||
// and extracts the elements into a slice of Network structs. In other words,
|
||||
// a generic collection is mapped into a relevant slice.
|
||||
func ExtractNetworks(page pagination.Page) ([]Network, error) {
|
||||
var resp struct {
|
||||
Networks []Network `mapstructure:"networks" json:"networks"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(page.(NetworkPage).Body, &resp)
|
||||
|
||||
return resp.Networks, err
|
||||
}
|
||||
31
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/urls.go
generated
vendored
Normal file
31
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/urls.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package networks
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL("networks", id)
|
||||
}
|
||||
|
||||
func rootURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("networks")
|
||||
}
|
||||
|
||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return resourceURL(c, id)
|
||||
}
|
||||
|
||||
func listURL(c *gophercloud.ServiceClient) string {
|
||||
return rootURL(c)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
38
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/urls_test.go
generated
vendored
Normal file
38
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/networks/urls_test.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package networks
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
const endpoint = "http://localhost:57909/"
|
||||
|
||||
func endpointClient() *gophercloud.ServiceClient {
|
||||
return &gophercloud.ServiceClient{Endpoint: endpoint, ResourceBase: endpoint + "v2.0/"}
|
||||
}
|
||||
|
||||
func TestGetURL(t *testing.T) {
|
||||
actual := getURL(endpointClient(), "foo")
|
||||
expected := endpoint + "v2.0/networks/foo"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestCreateURL(t *testing.T) {
|
||||
actual := createURL(endpointClient())
|
||||
expected := endpoint + "v2.0/networks"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestListURL(t *testing.T) {
|
||||
actual := createURL(endpointClient())
|
||||
expected := endpoint + "v2.0/networks"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestDeleteURL(t *testing.T) {
|
||||
actual := deleteURL(endpointClient(), "foo")
|
||||
expected := endpoint + "v2.0/networks/foo"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
8
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/doc.go
generated
vendored
Normal file
8
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/doc.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// 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
Normal file
11
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/errors.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package ports
|
||||
|
||||
import "fmt"
|
||||
|
||||
func err(str string) error {
|
||||
return fmt.Errorf("%s", str)
|
||||
}
|
||||
|
||||
var (
|
||||
errNetworkIDRequired = err("A Network ID is required")
|
||||
)
|
||||
225
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go
generated
vendored
Normal file
225
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
321
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests_test.go
generated
vendored
Normal file
321
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,321 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"ports": [
|
||||
{
|
||||
"status": "ACTIVE",
|
||||
"binding:host_id": "devstack",
|
||||
"name": "",
|
||||
"admin_state_up": true,
|
||||
"network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3",
|
||||
"tenant_id": "",
|
||||
"device_owner": "network:router_gateway",
|
||||
"mac_address": "fa:16:3e:58:42:ed",
|
||||
"fixed_ips": [
|
||||
{
|
||||
"subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062",
|
||||
"ip_address": "172.24.4.2"
|
||||
}
|
||||
],
|
||||
"id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
|
||||
"security_groups": [],
|
||||
"device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractPorts(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract subnets: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
expected := []Port{
|
||||
Port{
|
||||
Status: "ACTIVE",
|
||||
Name: "",
|
||||
AdminStateUp: true,
|
||||
NetworkID: "70c1db1f-b701-45bd-96e0-a313ee3430b3",
|
||||
TenantID: "",
|
||||
DeviceOwner: "network:router_gateway",
|
||||
MACAddress: "fa:16:3e:58:42:ed",
|
||||
FixedIPs: []IP{
|
||||
IP{
|
||||
SubnetID: "008ba151-0b8c-4a67-98b5-0d2b87666062",
|
||||
IPAddress: "172.24.4.2",
|
||||
},
|
||||
},
|
||||
ID: "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
|
||||
SecurityGroups: []string{},
|
||||
DeviceID: "9ae135f4-b6e0-4dad-9e91-3c223e385824",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"port": {
|
||||
"status": "ACTIVE",
|
||||
"name": "",
|
||||
"admin_state_up": true,
|
||||
"network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
|
||||
"tenant_id": "7e02058126cc4950b75f9970368ba177",
|
||||
"device_owner": "network:router_interface",
|
||||
"mac_address": "fa:16:3e:23:fd:d7",
|
||||
"fixed_ips": [
|
||||
{
|
||||
"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
|
||||
"ip_address": "10.0.0.1"
|
||||
}
|
||||
],
|
||||
"id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2",
|
||||
"security_groups": [],
|
||||
"device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
n, err := Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, n.Status, "ACTIVE")
|
||||
th.AssertEquals(t, n.Name, "")
|
||||
th.AssertEquals(t, n.AdminStateUp, true)
|
||||
th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
|
||||
th.AssertEquals(t, n.TenantID, "7e02058126cc4950b75f9970368ba177")
|
||||
th.AssertEquals(t, n.DeviceOwner, "network:router_interface")
|
||||
th.AssertEquals(t, n.MACAddress, "fa:16:3e:23:fd:d7")
|
||||
th.AssertDeepEquals(t, n.FixedIPs, []IP{
|
||||
IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"},
|
||||
})
|
||||
th.AssertEquals(t, n.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2")
|
||||
th.AssertDeepEquals(t, n.SecurityGroups, []string{})
|
||||
th.AssertEquals(t, n.Status, "ACTIVE")
|
||||
th.AssertEquals(t, n.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e")
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"port": {
|
||||
"network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
|
||||
"name": "private-port",
|
||||
"admin_state_up": true,
|
||||
"fixed_ips": [
|
||||
{
|
||||
"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
|
||||
"ip_address": "10.0.0.2"
|
||||
}
|
||||
],
|
||||
"security_groups": ["foo"]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"port": {
|
||||
"status": "DOWN",
|
||||
"name": "private-port",
|
||||
"allowed_address_pairs": [],
|
||||
"admin_state_up": true,
|
||||
"network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
|
||||
"tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
|
||||
"device_owner": "",
|
||||
"mac_address": "fa:16:3e:c9:cb:f0",
|
||||
"fixed_ips": [
|
||||
{
|
||||
"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
|
||||
"ip_address": "10.0.0.2"
|
||||
}
|
||||
],
|
||||
"id": "65c0ee9f-d634-4522-8954-51021b570b0d",
|
||||
"security_groups": [
|
||||
"f0ac4394-7e4a-4409-9701-ba8be283dbc3"
|
||||
],
|
||||
"device_id": ""
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
asu := true
|
||||
options := CreateOpts{
|
||||
Name: "private-port",
|
||||
AdminStateUp: &asu,
|
||||
NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7",
|
||||
FixedIPs: []IP{
|
||||
IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
|
||||
},
|
||||
SecurityGroups: []string{"foo"},
|
||||
}
|
||||
n, err := Create(fake.ServiceClient(), options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, n.Status, "DOWN")
|
||||
th.AssertEquals(t, n.Name, "private-port")
|
||||
th.AssertEquals(t, n.AdminStateUp, true)
|
||||
th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
|
||||
th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
|
||||
th.AssertEquals(t, n.DeviceOwner, "")
|
||||
th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0")
|
||||
th.AssertDeepEquals(t, n.FixedIPs, []IP{
|
||||
IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
|
||||
})
|
||||
th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
|
||||
th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
|
||||
}
|
||||
|
||||
func TestRequiredCreateOpts(t *testing.T) {
|
||||
res := Create(fake.ServiceClient(), CreateOpts{})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"port": {
|
||||
"name": "new_port_name",
|
||||
"fixed_ips": [
|
||||
{
|
||||
"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
|
||||
"ip_address": "10.0.0.3"
|
||||
}
|
||||
],
|
||||
"security_groups": [
|
||||
"f0ac4394-7e4a-4409-9701-ba8be283dbc3"
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"port": {
|
||||
"status": "DOWN",
|
||||
"name": "new_port_name",
|
||||
"admin_state_up": true,
|
||||
"network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
|
||||
"tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
|
||||
"device_owner": "",
|
||||
"mac_address": "fa:16:3e:c9:cb:f0",
|
||||
"fixed_ips": [
|
||||
{
|
||||
"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
|
||||
"ip_address": "10.0.0.3"
|
||||
}
|
||||
],
|
||||
"id": "65c0ee9f-d634-4522-8954-51021b570b0d",
|
||||
"security_groups": [
|
||||
"f0ac4394-7e4a-4409-9701-ba8be283dbc3"
|
||||
],
|
||||
"device_id": ""
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
options := UpdateOpts{
|
||||
Name: "new_port_name",
|
||||
FixedIPs: []IP{
|
||||
IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
|
||||
},
|
||||
SecurityGroups: []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"},
|
||||
}
|
||||
|
||||
s, err := Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, s.Name, "new_port_name")
|
||||
th.AssertDeepEquals(t, s.FixedIPs, []IP{
|
||||
IP{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
|
||||
})
|
||||
th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
126
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/results.go
generated
vendored
Normal file
126
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/results.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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
Normal file
31
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/urls.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
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)
|
||||
}
|
||||
44
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/urls_test.go
generated
vendored
Normal file
44
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/urls_test.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
const endpoint = "http://localhost:57909/"
|
||||
|
||||
func endpointClient() *gophercloud.ServiceClient {
|
||||
return &gophercloud.ServiceClient{Endpoint: endpoint, ResourceBase: endpoint + "v2.0/"}
|
||||
}
|
||||
|
||||
func TestListURL(t *testing.T) {
|
||||
actual := listURL(endpointClient())
|
||||
expected := endpoint + "v2.0/ports"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestGetURL(t *testing.T) {
|
||||
actual := getURL(endpointClient(), "foo")
|
||||
expected := endpoint + "v2.0/ports/foo"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestCreateURL(t *testing.T) {
|
||||
actual := createURL(endpointClient())
|
||||
expected := endpoint + "v2.0/ports"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestUpdateURL(t *testing.T) {
|
||||
actual := updateURL(endpointClient(), "foo")
|
||||
expected := endpoint + "v2.0/ports/foo"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestDeleteURL(t *testing.T) {
|
||||
actual := deleteURL(endpointClient(), "foo")
|
||||
expected := endpoint + "v2.0/ports/foo"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
10
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/doc.go
generated
vendored
Normal file
10
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/doc.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Package subnets contains functionality for working with Neutron subnet
|
||||
// resources. A subnet represents an IP address block that can be used to
|
||||
// assign IP addresses to virtual instances. Each subnet must have a CIDR and
|
||||
// must be associated with a network. IPs can either be selected from the whole
|
||||
// subnet CIDR or from allocation pools specified by the user.
|
||||
//
|
||||
// A subnet can also have a gateway, a list of DNS name servers, and host routes.
|
||||
// This information is pushed to instances whose interfaces are associated with
|
||||
// the subnet.
|
||||
package subnets
|
||||
13
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/errors.go
generated
vendored
Normal file
13
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/errors.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package subnets
|
||||
|
||||
import "fmt"
|
||||
|
||||
func err(str string) error {
|
||||
return fmt.Errorf("%s", str)
|
||||
}
|
||||
|
||||
var (
|
||||
errNetworkIDRequired = err("A network ID is required")
|
||||
errCIDRRequired = err("A valid CIDR is required")
|
||||
errInvalidIPType = err("An IP type must either be 4 or 6")
|
||||
)
|
||||
236
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/requests.go
generated
vendored
Normal file
236
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/requests.go
generated
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
package subnets
|
||||
|
||||
import (
|
||||
"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 {
|
||||
ToSubnetListQuery() (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 subnet attributes you want to see returned. SortKey allows you to sort
|
||||
// by a particular subnet attribute. SortDir sets the direction, and is either
|
||||
// `asc' or `desc'. Marker and Limit are used for pagination.
|
||||
type ListOpts struct {
|
||||
Name string `q:"name"`
|
||||
EnableDHCP *bool `q:"enable_dhcp"`
|
||||
NetworkID string `q:"network_id"`
|
||||
TenantID string `q:"tenant_id"`
|
||||
IPVersion int `q:"ip_version"`
|
||||
GatewayIP string `q:"gateway_ip"`
|
||||
CIDR string `q:"cidr"`
|
||||
ID string `q:"id"`
|
||||
Limit int `q:"limit"`
|
||||
Marker string `q:"marker"`
|
||||
SortKey string `q:"sort_key"`
|
||||
SortDir string `q:"sort_dir"`
|
||||
}
|
||||
|
||||
// ToSubnetListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToSubnetListQuery() (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
|
||||
// subnets. It accepts a ListOpts struct, which allows you to filter and sort
|
||||
// the returned collection for greater efficiency.
|
||||
//
|
||||
// Default policy settings return only those subnets 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.ToSubnetListQuery()
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
url += query
|
||||
}
|
||||
|
||||
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
|
||||
return SubnetPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
||||
// Get retrieves a specific subnet 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
|
||||
}
|
||||
|
||||
// Valid IP types
|
||||
const (
|
||||
IPv4 = 4
|
||||
IPv6 = 6
|
||||
)
|
||||
|
||||
// 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 {
|
||||
ToSubnetCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts represents the attributes used when creating a new subnet.
|
||||
type CreateOpts struct {
|
||||
// Required
|
||||
NetworkID string
|
||||
CIDR string
|
||||
// Optional
|
||||
Name string
|
||||
TenantID string
|
||||
AllocationPools []AllocationPool
|
||||
GatewayIP string
|
||||
IPVersion int
|
||||
EnableDHCP *bool
|
||||
DNSNameservers []string
|
||||
HostRoutes []HostRoute
|
||||
}
|
||||
|
||||
// ToSubnetCreateMap casts a CreateOpts struct to a map.
|
||||
func (opts CreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
|
||||
s := make(map[string]interface{})
|
||||
|
||||
if opts.NetworkID == "" {
|
||||
return nil, errNetworkIDRequired
|
||||
}
|
||||
if opts.CIDR == "" {
|
||||
return nil, errCIDRRequired
|
||||
}
|
||||
if opts.IPVersion != 0 && opts.IPVersion != IPv4 && opts.IPVersion != IPv6 {
|
||||
return nil, errInvalidIPType
|
||||
}
|
||||
|
||||
s["network_id"] = opts.NetworkID
|
||||
s["cidr"] = opts.CIDR
|
||||
|
||||
if opts.EnableDHCP != nil {
|
||||
s["enable_dhcp"] = &opts.EnableDHCP
|
||||
}
|
||||
if opts.Name != "" {
|
||||
s["name"] = opts.Name
|
||||
}
|
||||
if opts.GatewayIP != "" {
|
||||
s["gateway_ip"] = opts.GatewayIP
|
||||
}
|
||||
if opts.TenantID != "" {
|
||||
s["tenant_id"] = opts.TenantID
|
||||
}
|
||||
if opts.IPVersion != 0 {
|
||||
s["ip_version"] = opts.IPVersion
|
||||
}
|
||||
if len(opts.AllocationPools) != 0 {
|
||||
s["allocation_pools"] = opts.AllocationPools
|
||||
}
|
||||
if len(opts.DNSNameservers) != 0 {
|
||||
s["dns_nameservers"] = opts.DNSNameservers
|
||||
}
|
||||
if len(opts.HostRoutes) != 0 {
|
||||
s["host_routes"] = opts.HostRoutes
|
||||
}
|
||||
|
||||
return map[string]interface{}{"subnet": s}, nil
|
||||
}
|
||||
|
||||
// Create accepts a CreateOpts struct and creates a new subnet using the values
|
||||
// provided. You must remember to provide a valid NetworkID, CIDR and IP version.
|
||||
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||
var res CreateResult
|
||||
|
||||
reqBody, err := opts.ToSubnetCreateMap()
|
||||
if err != nil {
|
||||
res.Err = err
|
||||
return res
|
||||
}
|
||||
|
||||
_, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil)
|
||||
return res
|
||||
}
|
||||
|
||||
// UpdateOptsBuilder allows extensions to add additional parameters to the
|
||||
// Update request.
|
||||
type UpdateOptsBuilder interface {
|
||||
ToSubnetUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateOpts represents the attributes used when updating an existing subnet.
|
||||
type UpdateOpts struct {
|
||||
Name string
|
||||
GatewayIP string
|
||||
DNSNameservers []string
|
||||
HostRoutes []HostRoute
|
||||
EnableDHCP *bool
|
||||
}
|
||||
|
||||
// ToSubnetUpdateMap casts an UpdateOpts struct to a map.
|
||||
func (opts UpdateOpts) ToSubnetUpdateMap() (map[string]interface{}, error) {
|
||||
s := make(map[string]interface{})
|
||||
|
||||
if opts.EnableDHCP != nil {
|
||||
s["enable_dhcp"] = &opts.EnableDHCP
|
||||
}
|
||||
if opts.Name != "" {
|
||||
s["name"] = opts.Name
|
||||
}
|
||||
if opts.GatewayIP != "" {
|
||||
s["gateway_ip"] = opts.GatewayIP
|
||||
}
|
||||
if len(opts.DNSNameservers) != 0 {
|
||||
s["dns_nameservers"] = opts.DNSNameservers
|
||||
}
|
||||
if len(opts.HostRoutes) != 0 {
|
||||
s["host_routes"] = opts.HostRoutes
|
||||
}
|
||||
|
||||
return map[string]interface{}{"subnet": s}, nil
|
||||
}
|
||||
|
||||
// Update accepts a UpdateOpts struct and updates an existing subnet using the
|
||||
// values provided.
|
||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
|
||||
var res UpdateResult
|
||||
|
||||
reqBody, err := opts.ToSubnetUpdateMap()
|
||||
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 subnet associated with it.
|
||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
|
||||
var res DeleteResult
|
||||
_, res.Err = c.Delete(deleteURL(c, id), nil)
|
||||
return res
|
||||
}
|
||||
362
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/requests_test.go
generated
vendored
Normal file
362
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
package subnets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"subnets": [
|
||||
{
|
||||
"name": "private-subnet",
|
||||
"enable_dhcp": true,
|
||||
"network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
|
||||
"tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
|
||||
"dns_nameservers": [],
|
||||
"allocation_pools": [
|
||||
{
|
||||
"start": "10.0.0.2",
|
||||
"end": "10.0.0.254"
|
||||
}
|
||||
],
|
||||
"host_routes": [],
|
||||
"ip_version": 4,
|
||||
"gateway_ip": "10.0.0.1",
|
||||
"cidr": "10.0.0.0/24",
|
||||
"id": "08eae331-0402-425a-923c-34f7cfe39c1b"
|
||||
},
|
||||
{
|
||||
"name": "my_subnet",
|
||||
"enable_dhcp": true,
|
||||
"network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"dns_nameservers": [],
|
||||
"allocation_pools": [
|
||||
{
|
||||
"start": "192.0.0.2",
|
||||
"end": "192.255.255.254"
|
||||
}
|
||||
],
|
||||
"host_routes": [],
|
||||
"ip_version": 4,
|
||||
"gateway_ip": "192.0.0.1",
|
||||
"cidr": "192.0.0.0/8",
|
||||
"id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
count := 0
|
||||
|
||||
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
actual, err := ExtractSubnets(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract subnets: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
expected := []Subnet{
|
||||
Subnet{
|
||||
Name: "private-subnet",
|
||||
EnableDHCP: true,
|
||||
NetworkID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
|
||||
TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e",
|
||||
DNSNameservers: []string{},
|
||||
AllocationPools: []AllocationPool{
|
||||
AllocationPool{
|
||||
Start: "10.0.0.2",
|
||||
End: "10.0.0.254",
|
||||
},
|
||||
},
|
||||
HostRoutes: []HostRoute{},
|
||||
IPVersion: 4,
|
||||
GatewayIP: "10.0.0.1",
|
||||
CIDR: "10.0.0.0/24",
|
||||
ID: "08eae331-0402-425a-923c-34f7cfe39c1b",
|
||||
},
|
||||
Subnet{
|
||||
Name: "my_subnet",
|
||||
EnableDHCP: true,
|
||||
NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
TenantID: "4fd44f30292945e481c7b8a0c8908869",
|
||||
DNSNameservers: []string{},
|
||||
AllocationPools: []AllocationPool{
|
||||
AllocationPool{
|
||||
Start: "192.0.0.2",
|
||||
End: "192.255.255.254",
|
||||
},
|
||||
},
|
||||
HostRoutes: []HostRoute{},
|
||||
IPVersion: 4,
|
||||
GatewayIP: "192.0.0.1",
|
||||
CIDR: "192.0.0.0/8",
|
||||
ID: "54d6f61d-db07-451c-9ab3-b9609b6b6f0b",
|
||||
},
|
||||
}
|
||||
|
||||
th.CheckDeepEquals(t, expected, actual)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 page, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/subnets/54d6f61d-db07-451c-9ab3-b9609b6b6f0b", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "GET")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"subnet": {
|
||||
"name": "my_subnet",
|
||||
"enable_dhcp": true,
|
||||
"network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"dns_nameservers": [],
|
||||
"allocation_pools": [
|
||||
{
|
||||
"start": "192.0.0.2",
|
||||
"end": "192.255.255.254"
|
||||
}
|
||||
],
|
||||
"host_routes": [],
|
||||
"ip_version": 4,
|
||||
"gateway_ip": "192.0.0.1",
|
||||
"cidr": "192.0.0.0/8",
|
||||
"id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
s, err := Get(fake.ServiceClient(), "54d6f61d-db07-451c-9ab3-b9609b6b6f0b").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, s.Name, "my_subnet")
|
||||
th.AssertEquals(t, s.EnableDHCP, true)
|
||||
th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
|
||||
th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869")
|
||||
th.AssertDeepEquals(t, s.DNSNameservers, []string{})
|
||||
th.AssertDeepEquals(t, s.AllocationPools, []AllocationPool{
|
||||
AllocationPool{
|
||||
Start: "192.0.0.2",
|
||||
End: "192.255.255.254",
|
||||
},
|
||||
})
|
||||
th.AssertDeepEquals(t, s.HostRoutes, []HostRoute{})
|
||||
th.AssertEquals(t, s.IPVersion, 4)
|
||||
th.AssertEquals(t, s.GatewayIP, "192.0.0.1")
|
||||
th.AssertEquals(t, s.CIDR, "192.0.0.0/8")
|
||||
th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0b")
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "POST")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"subnet": {
|
||||
"network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
"ip_version": 4,
|
||||
"cidr": "192.168.199.0/24",
|
||||
"dns_nameservers": ["foo"],
|
||||
"allocation_pools": [
|
||||
{
|
||||
"start": "192.168.199.2",
|
||||
"end": "192.168.199.254"
|
||||
}
|
||||
],
|
||||
"host_routes": [{"destination":"","nexthop": "bar"}]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"subnet": {
|
||||
"name": "",
|
||||
"enable_dhcp": true,
|
||||
"network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"dns_nameservers": [],
|
||||
"allocation_pools": [
|
||||
{
|
||||
"start": "192.168.199.2",
|
||||
"end": "192.168.199.254"
|
||||
}
|
||||
],
|
||||
"host_routes": [],
|
||||
"ip_version": 4,
|
||||
"gateway_ip": "192.168.199.1",
|
||||
"cidr": "192.168.199.0/24",
|
||||
"id": "3b80198d-4f7b-4f77-9ef5-774d54e17126"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
opts := CreateOpts{
|
||||
NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
IPVersion: 4,
|
||||
CIDR: "192.168.199.0/24",
|
||||
AllocationPools: []AllocationPool{
|
||||
AllocationPool{
|
||||
Start: "192.168.199.2",
|
||||
End: "192.168.199.254",
|
||||
},
|
||||
},
|
||||
DNSNameservers: []string{"foo"},
|
||||
HostRoutes: []HostRoute{
|
||||
HostRoute{NextHop: "bar"},
|
||||
},
|
||||
}
|
||||
s, err := Create(fake.ServiceClient(), opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, s.Name, "")
|
||||
th.AssertEquals(t, s.EnableDHCP, true)
|
||||
th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
|
||||
th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869")
|
||||
th.AssertDeepEquals(t, s.DNSNameservers, []string{})
|
||||
th.AssertDeepEquals(t, s.AllocationPools, []AllocationPool{
|
||||
AllocationPool{
|
||||
Start: "192.168.199.2",
|
||||
End: "192.168.199.254",
|
||||
},
|
||||
})
|
||||
th.AssertDeepEquals(t, s.HostRoutes, []HostRoute{})
|
||||
th.AssertEquals(t, s.IPVersion, 4)
|
||||
th.AssertEquals(t, s.GatewayIP, "192.168.199.1")
|
||||
th.AssertEquals(t, s.CIDR, "192.168.199.0/24")
|
||||
th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126")
|
||||
}
|
||||
|
||||
func TestRequiredCreateOpts(t *testing.T) {
|
||||
res := Create(fake.ServiceClient(), CreateOpts{})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
|
||||
res = Create(fake.ServiceClient(), CreateOpts{NetworkID: "foo"})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
|
||||
res = Create(fake.ServiceClient(), CreateOpts{NetworkID: "foo", CIDR: "bar", IPVersion: 40})
|
||||
if res.Err == nil {
|
||||
t.Fatalf("Expected error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "PUT")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||
th.TestHeader(t, r, "Accept", "application/json")
|
||||
th.TestJSONRequest(t, r, `
|
||||
{
|
||||
"subnet": {
|
||||
"name": "my_new_subnet",
|
||||
"dns_nameservers": ["foo"],
|
||||
"host_routes": [{"destination":"","nexthop": "bar"}]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"subnet": {
|
||||
"name": "my_new_subnet",
|
||||
"enable_dhcp": true,
|
||||
"network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
|
||||
"tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
|
||||
"dns_nameservers": [],
|
||||
"allocation_pools": [
|
||||
{
|
||||
"start": "10.0.0.2",
|
||||
"end": "10.0.0.254"
|
||||
}
|
||||
],
|
||||
"host_routes": [],
|
||||
"ip_version": 4,
|
||||
"gateway_ip": "10.0.0.1",
|
||||
"cidr": "10.0.0.0/24",
|
||||
"id": "08eae331-0402-425a-923c-34f7cfe39c1b"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
opts := UpdateOpts{
|
||||
Name: "my_new_subnet",
|
||||
DNSNameservers: []string{"foo"},
|
||||
HostRoutes: []HostRoute{
|
||||
HostRoute{NextHop: "bar"},
|
||||
},
|
||||
}
|
||||
s, err := Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, s.Name, "my_new_subnet")
|
||||
th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b")
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) {
|
||||
th.TestMethod(t, r, "DELETE")
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
res := Delete(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b")
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
132
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/results.go
generated
vendored
Normal file
132
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/results.go
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
package subnets
|
||||
|
||||
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 subnet resource.
|
||||
func (r commonResult) Extract() (*Subnet, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Subnet *Subnet `json:"subnet"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(r.Body, &res)
|
||||
|
||||
return res.Subnet, 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
|
||||
}
|
||||
|
||||
// AllocationPool represents a sub-range of cidr available for dynamic
|
||||
// allocation to ports, e.g. {Start: "10.0.0.2", End: "10.0.0.254"}
|
||||
type AllocationPool struct {
|
||||
Start string `json:"start"`
|
||||
End string `json:"end"`
|
||||
}
|
||||
|
||||
// HostRoute represents a route that should be used by devices with IPs from
|
||||
// a subnet (not including local subnet route).
|
||||
type HostRoute struct {
|
||||
DestinationCIDR string `json:"destination"`
|
||||
NextHop string `json:"nexthop"`
|
||||
}
|
||||
|
||||
// Subnet represents a subnet. See package documentation for a top-level
|
||||
// description of what this is.
|
||||
type Subnet struct {
|
||||
// UUID representing the subnet
|
||||
ID string `mapstructure:"id" json:"id"`
|
||||
// UUID of the parent network
|
||||
NetworkID string `mapstructure:"network_id" json:"network_id"`
|
||||
// Human-readable name for the subnet. Might not be unique.
|
||||
Name string `mapstructure:"name" json:"name"`
|
||||
// IP version, either `4' or `6'
|
||||
IPVersion int `mapstructure:"ip_version" json:"ip_version"`
|
||||
// CIDR representing IP range for this subnet, based on IP version
|
||||
CIDR string `mapstructure:"cidr" json:"cidr"`
|
||||
// Default gateway used by devices in this subnet
|
||||
GatewayIP string `mapstructure:"gateway_ip" json:"gateway_ip"`
|
||||
// DNS name servers used by hosts in this subnet.
|
||||
DNSNameservers []string `mapstructure:"dns_nameservers" json:"dns_nameservers"`
|
||||
// Sub-ranges of CIDR available for dynamic allocation to ports. See AllocationPool.
|
||||
AllocationPools []AllocationPool `mapstructure:"allocation_pools" json:"allocation_pools"`
|
||||
// Routes that should be used by devices with IPs from this subnet (not including local subnet route).
|
||||
HostRoutes []HostRoute `mapstructure:"host_routes" json:"host_routes"`
|
||||
// Specifies whether DHCP is enabled for this subnet or not.
|
||||
EnableDHCP bool `mapstructure:"enable_dhcp" json:"enable_dhcp"`
|
||||
// Owner of network. Only admin users can specify a tenant_id other than its own.
|
||||
TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
|
||||
}
|
||||
|
||||
// SubnetPage is the page returned by a pager when traversing over a collection
|
||||
// of subnets.
|
||||
type SubnetPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// NextPageURL is invoked when a paginated collection of subnets 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 SubnetPage) NextPageURL() (string, error) {
|
||||
type resp struct {
|
||||
Links []gophercloud.Link `mapstructure:"subnets_links"`
|
||||
}
|
||||
|
||||
var r resp
|
||||
err := mapstructure.Decode(p.Body, &r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return gophercloud.ExtractNextURL(r.Links)
|
||||
}
|
||||
|
||||
// IsEmpty checks whether a SubnetPage struct is empty.
|
||||
func (p SubnetPage) IsEmpty() (bool, error) {
|
||||
is, err := ExtractSubnets(p)
|
||||
if err != nil {
|
||||
return true, nil
|
||||
}
|
||||
return len(is) == 0, nil
|
||||
}
|
||||
|
||||
// ExtractSubnets accepts a Page struct, specifically a SubnetPage struct,
|
||||
// and extracts the elements into a slice of Subnet structs. In other words,
|
||||
// a generic collection is mapped into a relevant slice.
|
||||
func ExtractSubnets(page pagination.Page) ([]Subnet, error) {
|
||||
var resp struct {
|
||||
Subnets []Subnet `mapstructure:"subnets" json:"subnets"`
|
||||
}
|
||||
|
||||
err := mapstructure.Decode(page.(SubnetPage).Body, &resp)
|
||||
|
||||
return resp.Subnets, err
|
||||
}
|
||||
31
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/urls.go
generated
vendored
Normal file
31
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/urls.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package subnets
|
||||
|
||||
import "github.com/rackspace/gophercloud"
|
||||
|
||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL("subnets", id)
|
||||
}
|
||||
|
||||
func rootURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("subnets")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
44
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/urls_test.go
generated
vendored
Normal file
44
vendor/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/urls_test.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package subnets
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
const endpoint = "http://localhost:57909/"
|
||||
|
||||
func endpointClient() *gophercloud.ServiceClient {
|
||||
return &gophercloud.ServiceClient{Endpoint: endpoint, ResourceBase: endpoint + "v2.0/"}
|
||||
}
|
||||
|
||||
func TestListURL(t *testing.T) {
|
||||
actual := listURL(endpointClient())
|
||||
expected := endpoint + "v2.0/subnets"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestGetURL(t *testing.T) {
|
||||
actual := getURL(endpointClient(), "foo")
|
||||
expected := endpoint + "v2.0/subnets/foo"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestCreateURL(t *testing.T) {
|
||||
actual := createURL(endpointClient())
|
||||
expected := endpoint + "v2.0/subnets"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestUpdateURL(t *testing.T) {
|
||||
actual := updateURL(endpointClient(), "foo")
|
||||
expected := endpoint + "v2.0/subnets/foo"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestDeleteURL(t *testing.T) {
|
||||
actual := deleteURL(endpointClient(), "foo")
|
||||
expected := endpoint + "v2.0/subnets/foo"
|
||||
th.AssertEquals(t, expected, actual)
|
||||
}
|
||||
Reference in New Issue
Block a user