From 63016155ea16eda3ecbb6dfef6ac2c72d30f8634 Mon Sep 17 00:00:00 2001 From: Kirill Shirinkin Date: Fri, 12 Feb 2016 18:57:41 +0100 Subject: [PATCH] Add distributed routers support --- Godeps/Godeps.json | 4 +- ...resource_openstack_networking_router_v2.go | 12 ++++++ ...rce_openstack_networking_router_v2_test.go | 2 + .../rackspace/gophercloud/openstack/client.go | 43 +++++++++++++++++-- .../v2/extensions/bootfromvolume/requests.go | 7 +++ .../openstack/compute/v2/servers/fixtures.go | 12 ++++++ .../openstack/compute/v2/servers/requests.go | 11 +++++ .../openstack/identity/v3/tokens/requests.go | 6 +-- .../v2/extensions/layer3/routers/requests.go | 7 +++ .../v2/extensions/layer3/routers/results.go | 3 ++ .../openstack/networking/v2/ports/requests.go | 38 +++++++++------- .../openstack/networking/v2/ports/results.go | 8 +++- .../rackspace/gophercloud/provider_client.go | 23 ++++++++++ .../rackspace/lb/v1/nodes/requests.go | 35 +++++++++++++++ .../r/networking_router_v2.html.markdown | 4 ++ 15 files changed, 190 insertions(+), 25 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index b627c36f3..75dcfe8bf 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -655,8 +655,8 @@ }, { "ImportPath": "github.com/rackspace/gophercloud", - "Comment": "v1.0.0-774-g680aa02", - "Rev": "680aa02616313d8399abc91f17a444cf9292f0e1" + "Comment": "v1.0.0-803-g6769c3b", + "Rev": "6769c3b3e54a5cf1b0bdb10ea5b25f5cff0a3134" }, { "ImportPath": "github.com/satori/go.uuid", diff --git a/builtin/providers/openstack/resource_openstack_networking_router_v2.go b/builtin/providers/openstack/resource_openstack_networking_router_v2.go index db488c031..fc0146a6e 100644 --- a/builtin/providers/openstack/resource_openstack_networking_router_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_router_v2.go @@ -37,6 +37,12 @@ func resourceNetworkingRouterV2() *schema.Resource { ForceNew: false, Computed: true, }, + "distributed": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Computed: true, + }, "external_gateway": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -69,6 +75,11 @@ func resourceNetworkingRouterV2Create(d *schema.ResourceData, meta interface{}) createOpts.AdminStateUp = &asu } + if dRaw, ok := d.GetOk("distributed"); ok { + d := dRaw.(bool) + createOpts.Distributed = &d + } + externalGateway := d.Get("external_gateway").(string) if externalGateway != "" { gatewayInfo := routers.GatewayInfo{ @@ -126,6 +137,7 @@ func resourceNetworkingRouterV2Read(d *schema.ResourceData, meta interface{}) er d.Set("name", n.Name) d.Set("admin_state_up", n.AdminStateUp) + d.Set("distributed", n.Distributed) d.Set("tenant_id", n.TenantID) d.Set("external_gateway", n.GatewayInfo.NetworkID) diff --git a/builtin/providers/openstack/resource_openstack_networking_router_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_router_v2_test.go index 248f4e721..fd0ff0cc7 100644 --- a/builtin/providers/openstack/resource_openstack_networking_router_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_router_v2_test.go @@ -91,10 +91,12 @@ var testAccNetworkingV2Router_basic = fmt.Sprintf(` resource "openstack_networking_router_v2" "foo" { name = "router" admin_state_up = "true" + distributed = "false" }`) var testAccNetworkingV2Router_update = fmt.Sprintf(` resource "openstack_networking_router_v2" "foo" { name = "router_2" admin_state_up = "true" + distributed = "false" }`) diff --git a/vendor/github.com/rackspace/gophercloud/openstack/client.go b/vendor/github.com/rackspace/gophercloud/openstack/client.go index 33602a696..baa4cb51c 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/client.go +++ b/vendor/github.com/rackspace/gophercloud/openstack/client.go @@ -3,6 +3,7 @@ package openstack import ( "fmt" "net/url" + "strings" "github.com/rackspace/gophercloud" tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens" @@ -64,8 +65,8 @@ func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.Provider // Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint. func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { versions := []*utils.Version{ - &utils.Version{ID: v20, Priority: 20, Suffix: "/v2.0/"}, - &utils.Version{ID: v30, Priority: 30, Suffix: "/v3/"}, + {ID: v20, Priority: 20, Suffix: "/v2.0/"}, + {ID: v30, Priority: 30, Suffix: "/v3/"}, } chosen, endpoint, err := utils.ChooseVersion(client, versions) @@ -110,7 +111,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc if options.AllowReauth { client.ReauthFunc = func() error { client.TokenID = "" - return AuthenticateV2(client, options) + return v2auth(client, endpoint, options) } } client.TokenID = token.ID @@ -168,7 +169,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, options gopherc if options.AllowReauth { client.ReauthFunc = func() error { client.TokenID = "" - return AuthenticateV3(client, options) + return v3auth(client, endpoint, options) } } client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { @@ -198,6 +199,40 @@ func NewIdentityV3(client *gophercloud.ProviderClient) *gophercloud.ServiceClien } } +func NewIdentityAdminV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + eo.ApplyDefaults("identity") + eo.Availability = gophercloud.AvailabilityAdmin + + url, err := client.EndpointLocator(eo) + if err != nil { + return nil, err + } + + // Force using v2 API + if strings.Contains(url, "/v3") { + url = strings.Replace(url, "/v3", "/v2.0", -1) + } + + return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil +} + +func NewIdentityAdminV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + eo.ApplyDefaults("identity") + eo.Availability = gophercloud.AvailabilityAdmin + + url, err := client.EndpointLocator(eo) + if err != nil { + return nil, err + } + + // Force using v3 API + if strings.Contains(url, "/v2.0") { + url = strings.Replace(url, "/v2.0", "/v3", -1) + } + + return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil +} + // NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package. func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { eo.ApplyDefaults("object-store") diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go index c8edee0e8..dceff3d87 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go +++ b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go @@ -15,6 +15,7 @@ const ( Volume SourceType = "volume" Snapshot SourceType = "snapshot" Image SourceType = "image" + Blank SourceType = "blank" ) // BlockDevice is a structure with options for booting a server instance @@ -32,6 +33,9 @@ type BlockDevice struct { // and "local". DestinationType string `json:"destination_type"` + // GuestFormat [optional] specifies the format of the block device. + GuestFormat string `json:"guest_format"` + // SourceType [required] must be one of: "volume", "snapshot", "image". SourceType SourceType `json:"source_type"` @@ -82,6 +86,9 @@ func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { if bd.DestinationType != "" { blockDevice[i]["destination_type"] = bd.DestinationType } + if bd.GuestFormat != "" { + blockDevice[i]["guest_format"] = bd.GuestFormat + } } serverMap["block_device_mapping_v2"] = blockDevice diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go index 4339a16d4..151fea27b 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go +++ b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go @@ -399,6 +399,18 @@ func HandleServerDeletionSuccessfully(t *testing.T) { }) } +// HandleAdminPasswordChangeSuccessfully sets up the test server to respond to a server password +// change request. +func HandleServerForceDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers/asdfasdfasdf/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, `{ "forceDelete": "" }`) + + w.WriteHeader(http.StatusAccepted) + }) +} + // HandleServerGetSuccessfully sets up the test server to respond to a server Get request. func HandleServerGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) { diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go index f9839d9f2..8e60daa69 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go +++ b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go @@ -303,6 +303,17 @@ func Delete(client *gophercloud.ServiceClient, id string) DeleteResult { return res } +func ForceDelete(client *gophercloud.ServiceClient, id string) ActionResult { + var req struct { + ForceDelete string `json:"forceDelete"` + } + + var res ActionResult + _, res.Err = client.Post(actionURL(client, id), req, nil, nil) + return res + +} + // Get requests details on a single server, by ID. func Get(client *gophercloud.ServiceClient, id string) GetResult { var result GetResult diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/requests.go b/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/requests.go index d449ca36e..d63b1bb51 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/requests.go +++ b/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/requests.go @@ -15,9 +15,9 @@ type Scope struct { } func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string { - h := c.AuthenticatedHeaders() - h["X-Subject-Token"] = subjectToken - return h + return map[string]string{ + "X-Subject-Token": subjectToken, + } } // Create authenticates and either generates a new token, or changes the Scope of an existing token. diff --git a/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go b/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go index 8b6e73de9..1ffc1369b 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -16,6 +16,7 @@ type ListOpts struct { ID string `q:"id"` Name string `q:"name"` AdminStateUp *bool `q:"admin_state_up"` + Distributed *bool `q:"distributed"` Status string `q:"status"` TenantID string `q:"tenant_id"` Limit int `q:"limit"` @@ -46,6 +47,7 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { type CreateOpts struct { Name string AdminStateUp *bool + Distributed *bool TenantID string GatewayInfo *GatewayInfo } @@ -62,6 +64,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult { type router struct { Name *string `json:"name,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` + Distributed *bool `json:"distributed,omitempty"` TenantID *string `json:"tenant_id,omitempty"` GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"` } @@ -73,6 +76,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult { reqBody := request{Router: router{ Name: gophercloud.MaybeString(opts.Name), AdminStateUp: opts.AdminStateUp, + Distributed: opts.Distributed, TenantID: gophercloud.MaybeString(opts.TenantID), }} @@ -96,6 +100,7 @@ func Get(c *gophercloud.ServiceClient, id string) GetResult { type UpdateOpts struct { Name string AdminStateUp *bool + Distributed *bool GatewayInfo *GatewayInfo Routes []Route } @@ -109,6 +114,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu type router struct { Name *string `json:"name,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` + Distributed *bool `json:"distributed,omitempty"` GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"` Routes []Route `json:"routes"` } @@ -120,6 +126,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu reqBody := request{Router: router{ Name: gophercloud.MaybeString(opts.Name), AdminStateUp: opts.AdminStateUp, + Distributed: opts.Distributed, }} if opts.GatewayInfo != nil { diff --git a/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/results.go b/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/results.go index 5e297ab26..453412398 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/results.go @@ -35,6 +35,9 @@ type Router struct { // Administrative state of the router. AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"` + // Whether router is disitrubted or not.. + Distributed bool `json:"distributed" mapstructure:"distributed"` + // Human readable name for the router. Does not have to be unique. Name string `json:"name" mapstructure:"name"` diff --git a/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go b/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go index 2caf1cace..e73e10ac6 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go +++ b/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go @@ -95,15 +95,16 @@ type CreateOptsBuilder interface { // 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 + NetworkID string + Name string + AdminStateUp *bool + MACAddress string + FixedIPs interface{} + DeviceID string + DeviceOwner string + TenantID string + SecurityGroups []string + AllowedAddressPairs []AddressPair } // ToPortCreateMap casts a CreateOpts struct to a map. @@ -139,6 +140,9 @@ func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) { if opts.MACAddress != "" { p["mac_address"] = opts.MACAddress } + if opts.AllowedAddressPairs != nil { + p["allowed_address_pairs"] = opts.AllowedAddressPairs + } return map[string]interface{}{"port": p}, nil } @@ -168,12 +172,13 @@ type UpdateOptsBuilder interface { // 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 + Name string + AdminStateUp *bool + FixedIPs interface{} + DeviceID string + DeviceOwner string + SecurityGroups []string + AllowedAddressPairs []AddressPair } // ToPortUpdateMap casts an UpdateOpts struct to a map. @@ -198,6 +203,9 @@ func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) { if opts.Name != "" { p["name"] = opts.Name } + if opts.AllowedAddressPairs != nil { + p["allowed_address_pairs"] = opts.AllowedAddressPairs + } return map[string]interface{}{"port": p}, nil } diff --git a/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/results.go b/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/results.go index 2511ff53b..1f7eea169 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/results.go +++ b/vendor/github.com/rackspace/gophercloud/openstack/networking/v2/ports/results.go @@ -19,7 +19,6 @@ func (r commonResult) Extract() (*Port, error) { var res struct { Port *Port `json:"port"` } - err := mapstructure.Decode(r.Body, &res) return res.Port, err @@ -51,6 +50,11 @@ type IP struct { IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"` } +type AddressPair struct { + IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"` + MACAddress string `mapstructure:"mac_address" json:"mac_address,omitempty"` +} + // Port represents a Neutron port. See package documentation for a top-level // description of what this is. type Port struct { @@ -78,6 +82,8 @@ type Port struct { SecurityGroups []string `mapstructure:"security_groups" json:"security_groups"` // Identifies the device (e.g., virtual server) using this port. DeviceID string `mapstructure:"device_id" json:"device_id"` + // Identifies the list of IP addresses the port will recognize/accept + AllowedAddressPairs []AddressPair `mapstructure:"allowed_address_pairs" json:"allowed_address_pairs"` } // PortPage is the page returned by a pager when traversing over a collection diff --git a/vendor/github.com/rackspace/gophercloud/provider_client.go b/vendor/github.com/rackspace/gophercloud/provider_client.go index 92643556f..53fce7370 100644 --- a/vendor/github.com/rackspace/gophercloud/provider_client.go +++ b/vendor/github.com/rackspace/gophercloud/provider_client.go @@ -177,6 +177,9 @@ func (client *ProviderClient) Request(method, url string, options RequestOpts) ( } } + // Set connection parameter to close the connection immediately when we've got the response + req.Close = true + // Issue the request. resp, err := client.HTTPClient.Do(req) if err != nil { @@ -246,6 +249,8 @@ func defaultOkCodes(method string) []int { return []int{201, 202} case method == "PUT": return []int{201, 202} + case method == "PATCH": + return []int{200, 204} case method == "DELETE": return []int{202, 204} } @@ -299,6 +304,24 @@ func (client *ProviderClient) Put(url string, JSONBody interface{}, JSONResponse return client.Request("PUT", url, *opts) } +func (client *ProviderClient) Patch(url string, JSONBody interface{}, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) { + if opts == nil { + opts = &RequestOpts{} + } + + if v, ok := (JSONBody).(io.ReadSeeker); ok { + opts.RawBody = v + } else if JSONBody != nil { + opts.JSONBody = JSONBody + } + + if JSONResponse != nil { + opts.JSONResponse = JSONResponse + } + + return client.Request("PATCH", url, *opts) +} + func (client *ProviderClient) Delete(url string, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = &RequestOpts{} diff --git a/vendor/github.com/rackspace/gophercloud/rackspace/lb/v1/nodes/requests.go b/vendor/github.com/rackspace/gophercloud/rackspace/lb/v1/nodes/requests.go index dc2d46c89..9da376f83 100644 --- a/vendor/github.com/rackspace/gophercloud/rackspace/lb/v1/nodes/requests.go +++ b/vendor/github.com/rackspace/gophercloud/rackspace/lb/v1/nodes/requests.go @@ -249,3 +249,38 @@ func ListEvents(client *gophercloud.ServiceClient, loadBalancerID int, opts List return NodeEventPage{pagination.SinglePageBase(r)} }) } + +// GetByIPPort locates a load balancer node by IP and port. +func GetByIPPort( + client *gophercloud.ServiceClient, + loadBalancerID int, + address string, + port int, +) (*Node, error) { + + // nil until found + var found *Node + + List(client, loadBalancerID, nil).EachPage(func(page pagination.Page) (bool, error) { + lbNodes, err := ExtractNodes(page) + if err != nil { + return false, err + } + + for _, trialNode := range lbNodes { + if trialNode.Address == address && trialNode.Port == port { + found = &trialNode + return false, nil + } + + } + + return true, nil + }) + + if found == nil { + return nil, errors.New("Unable to get node by IP and Port") + } + + return found, nil +} diff --git a/website/source/docs/providers/openstack/r/networking_router_v2.html.markdown b/website/source/docs/providers/openstack/r/networking_router_v2.html.markdown index c1a97aa28..04a261a38 100644 --- a/website/source/docs/providers/openstack/r/networking_router_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/networking_router_v2.html.markdown @@ -36,6 +36,10 @@ The following arguments are supported: (must be "true" or "false" if provided). Changing this updates the `admin_state_up` of an existing router. +* `distributed` - (Optional) Indicates whether or not to create a + distributed router. The default policy setting in Neutron restricts + usage of this property to administrative users only. + * `external_gateway` - (Optional) The network UUID of an external gateway for the router. A router with an external gateway is required if any compute instances or load balancers will be using floating IPs. Changing this