Merge pull request #49785 from FengyunPan/fix-getPortByIP

Automatic merge from submit-queue (batch tested with PRs 47724, 49984, 49785, 49803, 49618)

Fix conflict about getPortByIp

**What this PR does / why we need it**:
Currently getPortByIp() get port of instance only based on IP.
If there are two instances in diffent network and the CIDR of
their subnet are same, getPortByIp() will be conflict.
My PR gets port based on IP and Name of instance.

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #
Fix #43909

**Special notes for your reviewer**:

**Release note**:
```release-note
NONE
```
This commit is contained in:
Kubernetes Submit Queue
2017-08-11 18:47:19 -07:00
committed by GitHub
22 changed files with 655 additions and 78 deletions

View File

@@ -36,6 +36,7 @@ filegroup(
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes:all-srcs",
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:all-srcs",
"//vendor/github.com/gophercloud/gophercloud/openstack/common/extensions:all-srcs",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces:all-srcs",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach:all-srcs",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors:all-srcs",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images:all-srcs",

View File

@@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"requests.go",
"results.go",
"urls.go",
],
tags = ["automanaged"],
deps = [
"//vendor/github.com/gophercloud/gophercloud:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/pagination:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@@ -0,0 +1,3 @@
// Package attachinterfaces provides the ability to manage network interfaces through
// nova-network
package attachinterfaces

View File

@@ -0,0 +1,13 @@
package attachinterfaces
import (
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
// List makes a request against the nova API to list the servers interfaces.
func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
return pagination.NewPager(client, listInterfaceURL(client, serverID), func(r pagination.PageResult) pagination.Page {
return InterfacePage{pagination.SinglePageBase(r)}
})
}

View File

@@ -0,0 +1,43 @@
package attachinterfaces
import (
"github.com/gophercloud/gophercloud/pagination"
)
// FixedIP represents a Fixed IP Address.
type FixedIP struct {
SubnetID string `json:"subnet_id"`
IPAddress string `json:"ip_address"`
}
// Interface represents a network interface on an instance.
type Interface struct {
PortState string `json:"port_state"`
FixedIPs []FixedIP `json:"fixed_ips"`
PortID string `json:"port_id"`
NetID string `json:"net_id"`
MACAddr string `json:"mac_addr"`
}
// InterfacePage abstracts the raw results of making a List() request against the API.
// As OpenStack extensions may freely alter the response bodies of structures returned
// to the client, you may only safely access the data provided through the ExtractInterfaces call.
type InterfacePage struct {
pagination.SinglePageBase
}
// IsEmpty returns true if an InterfacePage contains no interfaces.
func (r InterfacePage) IsEmpty() (bool, error) {
interfaces, err := ExtractInterfaces(r)
return len(interfaces) == 0, err
}
// ExtractInterfaces interprets the results of a single page from a List() call,
// producing a map of interfaces.
func ExtractInterfaces(r pagination.Page) ([]Interface, error) {
var s struct {
Interfaces []Interface `json:"interfaceAttachments"`
}
err := (r.(InterfacePage)).ExtractInto(&s)
return s.Interfaces, err
}

View File

@@ -0,0 +1,7 @@
package attachinterfaces
import "github.com/gophercloud/gophercloud"
func listInterfaceURL(client *gophercloud.ServiceClient, serverID string) string {
return client.ServiceURL("servers", serverID, "os-interface")
}

View File

@@ -50,6 +50,9 @@ type ListOpts struct {
// Bool to show all tenants
AllTenants bool `q:"all_tenants"`
// List servers for a particular tenant. Setting "AllTenants = true" is required.
TenantID string `q:"tenant_id"`
}
// ToServerListQuery formats a ListOpts into a query string.

View File

@@ -27,3 +27,81 @@ func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
return TenantPage{pagination.LinkedPageBase{PageResult: r}}
})
}
// CreateOpts represents the options needed when creating new tenant.
type CreateOpts struct {
// Name is the name of the tenant.
Name string `json:"name" required:"true"`
// Description is the description of the tenant.
Description string `json:"description,omitempty"`
// Enabled sets the tenant status to enabled or disabled.
Enabled *bool `json:"enabled,omitempty"`
}
// CreateOptsBuilder describes struct types that can be accepted by the Create call.
type CreateOptsBuilder interface {
ToTenantCreateMap() (map[string]interface{}, error)
}
// ToTenantCreateMap assembles a request body based on the contents of a CreateOpts.
func (opts CreateOpts) ToTenantCreateMap() (map[string]interface{}, error) {
return gophercloud.BuildRequestBody(opts, "tenant")
}
// Create is the operation responsible for creating new tenant.
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
b, err := opts.ToTenantCreateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200, 201},
})
return
}
// Get requests details on a single tenant by ID.
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
return
}
// UpdateOptsBuilder allows extensions to add additional attributes to the Update request.
type UpdateOptsBuilder interface {
ToTenantUpdateMap() (map[string]interface{}, error)
}
// UpdateOpts specifies the base attributes that may be updated on an existing server.
type UpdateOpts struct {
// Name is the name of the tenant.
Name string `json:"name,omitempty"`
// Description is the description of the tenant.
Description string `json:"description,omitempty"`
// Enabled sets the tenant status to enabled or disabled.
Enabled *bool `json:"enabled,omitempty"`
}
// ToTenantUpdateMap formats an UpdateOpts structure into a request body.
func (opts UpdateOpts) ToTenantUpdateMap() (map[string]interface{}, error) {
return gophercloud.BuildRequestBody(opts, "tenant")
}
// Update is the operation responsible for updating exist tenants by their TenantID.
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
b, err := opts.ToTenantUpdateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
return
}
// Delete is the operation responsible for permanently deleting an API tenant.
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
_, r.Err = client.Delete(deleteURL(client, id), nil)
return
}

View File

@@ -51,3 +51,36 @@ func ExtractTenants(r pagination.Page) ([]Tenant, error) {
err := (r.(TenantPage)).ExtractInto(&s)
return s.Tenants, err
}
type tenantResult struct {
gophercloud.Result
}
// Extract interprets any tenantResults as a tenant.
func (r tenantResult) Extract() (*Tenant, error) {
var s struct {
Tenant *Tenant `json:"tenant"`
}
err := r.ExtractInto(&s)
return s.Tenant, err
}
// GetResult temporarily contains the response from the Get call.
type GetResult struct {
tenantResult
}
// CreateResult temporarily contains the reponse from the Create call.
type CreateResult struct {
tenantResult
}
// DeleteResult temporarily contains the response from the Delete call.
type DeleteResult struct {
gophercloud.ErrResult
}
// UpdateResult temporarily contains the response from the Update call.
type UpdateResult struct {
tenantResult
}

View File

@@ -5,3 +5,19 @@ import "github.com/gophercloud/gophercloud"
func listURL(client *gophercloud.ServiceClient) string {
return client.ServiceURL("tenants")
}
func getURL(client *gophercloud.ServiceClient, tenantID string) string {
return client.ServiceURL("tenants", tenantID)
}
func createURL(client *gophercloud.ServiceClient) string {
return client.ServiceURL("tenants")
}
func deleteURL(client *gophercloud.ServiceClient, tenantID string) string {
return client.ServiceURL("tenants", tenantID)
}
func updateURL(client *gophercloud.ServiceClient, tenantID string) string {
return client.ServiceURL("tenants", tenantID)
}

View File

@@ -182,7 +182,7 @@ func Get(c *gophercloud.ServiceClient, token string) (r GetResult) {
func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
resp, err := c.Request("HEAD", tokenURL(c), &gophercloud.RequestOpts{
MoreHeaders: subjectTokenHeaders(c, token),
OkCodes: []int{200, 204, 400, 401, 403, 404},
OkCodes: []int{200, 204, 404},
})
if err != nil {
return false, err

View File

@@ -41,6 +41,32 @@ type ServiceCatalog struct {
Entries []CatalogEntry `json:"catalog"`
}
// Domain provides information about the domain to which this token grants access.
type Domain struct {
ID string `json:"id"`
Name string `json:"name"`
}
// User represents a user resource that exists on the API.
type User struct {
Domain Domain `json:"domain"`
ID string `json:"id"`
Name string `json:"name"`
}
// Role provides information about roles to which User is authorized.
type Role struct {
ID string `json:"id"`
Name string `json:"name"`
}
// Project provides information about project to which User is authorized.
type Project struct {
Domain Domain `json:"domain"`
ID string `json:"id"`
Name string `json:"name"`
}
// commonResult is the deferred result of a Create or a Get call.
type commonResult struct {
gophercloud.Result
@@ -67,12 +93,39 @@ func (r commonResult) ExtractToken() (*Token, error) {
}
// ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token.
func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
var s ServiceCatalog
err := r.ExtractInto(&s)
return &s, err
}
// ExtractUser returns the User that is the owner of the Token.
func (r commonResult) ExtractUser() (*User, error) {
var s struct {
User *User `json:"user"`
}
err := r.ExtractInto(&s)
return s.User, err
}
// ExtractRoles returns Roles to which User is authorized.
func (r commonResult) ExtractRoles() ([]Role, error) {
var s struct {
Roles []Role `json:"roles"`
}
err := r.ExtractInto(&s)
return s.Roles, err
}
// ExtractProject returns Project to which User is authorized.
func (r commonResult) ExtractProject() (*Project, error) {
var s struct {
Project *Project `json:"project"`
}
err := r.ExtractInto(&s)
return s.Project, err
}
// CreateResult defers the interpretation of a created token.
// Use ExtractToken() to interpret it as a Token, or ExtractServiceCatalog() to interpret it as a service catalog.
type CreateResult struct {

View File

@@ -22,6 +22,7 @@ type ListOpts struct {
SortKey string `q:"sort_key"`
SortDir string `q:"sort_dir"`
RouterID string `q:"router_id"`
Status string `q:"status"`
}
// List returns a Pager which allows you to iterate over a collection of

View File

@@ -78,6 +78,53 @@ func (r Result) extractIntoPtr(to interface{}, label string) error {
return err
}
toValue := reflect.ValueOf(to)
if toValue.Kind() == reflect.Ptr {
toValue = toValue.Elem()
}
switch toValue.Kind() {
case reflect.Slice:
typeOfV := toValue.Type().Elem()
if typeOfV.Kind() == reflect.Struct {
if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous {
newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0)
newType := reflect.New(typeOfV).Elem()
for _, v := range m[label].([]interface{}) {
b, err := json.Marshal(v)
if err != nil {
return err
}
for i := 0; i < newType.NumField(); i++ {
s := newType.Field(i).Addr().Interface()
err = json.NewDecoder(bytes.NewReader(b)).Decode(s)
if err != nil {
return err
}
}
newSlice = reflect.Append(newSlice, newType)
}
toValue.Set(newSlice)
}
}
case reflect.Struct:
typeOfV := toValue.Type()
if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous {
for i := 0; i < toValue.NumField(); i++ {
toField := toValue.Field(i)
if toField.Kind() == reflect.Struct {
s := toField.Addr().Interface()
err = json.NewDecoder(bytes.NewReader(b)).Decode(s)
if err != nil {
return err
}
}
}
}
}
err = json.Unmarshal(b, &to)
return err
}