implement network task for OpenStack platform

This commit is contained in:
zengchen1024 2018-03-28 17:59:55 +08:00
parent b8793930fa
commit ffbd81ee7c
10 changed files with 630 additions and 0 deletions

View File

@ -20,6 +20,7 @@ go_library(
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
],

View File

@ -26,6 +26,7 @@ import (
cinder "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
sg "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
sgr "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kops/dnsprovider/pkg/dnsprovider"
@ -81,6 +82,12 @@ type OpenstackCloud interface {
//CreateSecurityGroupRule will create a new Neutron security group rule
CreateSecurityGroupRule(opt sgr.CreateOpts) (*sgr.SecGroupRule, error)
//ListNetworks will return the Neutron networks which match the options
ListNetworks(opt networks.ListOptsBuilder) ([]networks.Network, error)
//CreateNetwork will create a new Neutron network
CreateNetwork(opt networks.CreateOptsBuilder) (*networks.Network, error)
}
type openstackCloud struct {
@ -317,3 +324,48 @@ func (c *openstackCloud) CreateSecurityGroupRule(opt sgr.CreateOpts) (*sgr.SecGr
return rule, wait.ErrWaitTimeout
}
}
func (c *openstackCloud) ListNetworks(opt networks.ListOptsBuilder) ([]networks.Network, error) {
var ns []networks.Network
done, err := vfs.RetryWithBackoff(readBackoff, func() (bool, error) {
allPages, err := networks.List(c.neutronClient, opt).AllPages()
if err != nil {
return false, fmt.Errorf("error listing networks: %v", err)
}
r, err := networks.ExtractNetworks(allPages)
if err != nil {
return false, fmt.Errorf("error extracting networks from pages: %v", err)
}
ns = r
return true, nil
})
if err != nil {
return ns, err
} else if done {
return ns, nil
} else {
return ns, wait.ErrWaitTimeout
}
}
func (c *openstackCloud) CreateNetwork(opt networks.CreateOptsBuilder) (*networks.Network, error) {
var n *networks.Network
done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) {
r, err := networks.Create(c.neutronClient, opt).Extract()
if err != nil {
return false, fmt.Errorf("error creating network: %v", err)
}
n = r
return true, nil
})
if err != nil {
return n, err
} else if done {
return n, nil
} else {
return n, wait.ErrWaitTimeout
}
}

View File

@ -3,6 +3,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"network.go",
"network_fitask.go",
"securitygroup.go",
"securitygroup_fitask.go",
"securitygrouprule.go",
@ -18,5 +20,6 @@ go_library(
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks:go_default_library",
],
)

View File

@ -0,0 +1,110 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package openstacktasks
import (
"fmt"
"github.com/golang/glog"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
//go:generate fitask -type=Network
type Network struct {
ID *string
Name *string
Lifecycle *fi.Lifecycle
}
var _ fi.CompareWithID = &Network{}
func (n *Network) CompareWithID() *string {
return n.ID
}
func (n *Network) Find(context *fi.Context) (*Network, error) {
cloud := context.Cloud.(openstack.OpenstackCloud)
opt := networks.ListOpts{
Name: fi.StringValue(n.Name),
ID: fi.StringValue(n.ID),
}
ns, err := cloud.ListNetworks(opt)
if err != nil {
return nil, err
}
if ns == nil {
return nil, nil
} else if len(ns) != 1 {
return nil, fmt.Errorf("found multiple networks with name: %s", fi.StringValue(n.Name))
}
v := ns[0]
actual := &Network{
ID: fi.String(v.ID),
Name: fi.String(v.Name),
Lifecycle: n.Lifecycle,
}
return actual, nil
}
func (c *Network) Run(context *fi.Context) error {
return fi.DefaultDeltaRunMethod(c, context)
}
func (_ *Network) CheckChanges(a, e, changes *Network) error {
if a == nil {
if e.Name == nil {
return fi.RequiredField("Name")
}
} else {
if changes.ID != nil {
return fi.CannotChangeField("ID")
}
if changes.Name != nil {
return fi.CannotChangeField("Name")
}
}
return nil
}
func (_ *Network) ShouldCreate(a, e, changes *Network) (bool, error) {
return a == nil, nil
}
func (_ *Network) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, changes *Network) error {
if a == nil {
glog.V(2).Infof("Creating Network with name:%q", fi.StringValue(e.Name))
opt := networks.CreateOpts{
Name: fi.StringValue(e.Name),
AdminStateUp: fi.Bool(true),
}
v, err := t.Cloud.CreateNetwork(opt)
if err != nil {
return fmt.Errorf("Error creating network: %v", err)
}
e.ID = fi.String(v.ID)
glog.V(2).Infof("Creating a new Openstack network, id=%s", v.ID)
return nil
}
glog.V(2).Infof("Openstack task Network::RenderOpenstack did nothing")
return nil
}

View File

@ -0,0 +1,75 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by ""fitask" -type=Network"; DO NOT EDIT
package openstacktasks
import (
"encoding/json"
"k8s.io/kops/upup/pkg/fi"
)
// Network
// JSON marshalling boilerplate
type realNetwork Network
// UnmarshalJSON implements conversion to JSON, supporitng an alternate specification of the object as a string
func (o *Network) UnmarshalJSON(data []byte) error {
var jsonName string
if err := json.Unmarshal(data, &jsonName); err == nil {
o.Name = &jsonName
return nil
}
var r realNetwork
if err := json.Unmarshal(data, &r); err != nil {
return err
}
*o = Network(r)
return nil
}
var _ fi.HasLifecycle = &Network{}
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
func (o *Network) GetLifecycle() *fi.Lifecycle {
return o.Lifecycle
}
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
func (o *Network) SetLifecycle(lifecycle fi.Lifecycle) {
o.Lifecycle = &lifecycle
}
var _ fi.HasName = &Network{}
// GetName returns the Name of the object, implementing fi.HasName
func (o *Network) GetName() *string {
return o.Name
}
// SetName sets the Name of the object, implementing fi.SetName
func (o *Network) SetName(name string) {
o.Name = &name
}
// String is the stringer function for the task, producing readable output using fi.TaskAsString
func (o *Network) String() string {
return fi.TaskAsString(o)
}

View File

@ -0,0 +1,17 @@
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",
],
importpath = "github.com/gophercloud/gophercloud/openstack/networking/v2/networks",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/gophercloud/gophercloud:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/pagination:go_default_library",
],
)

View File

@ -0,0 +1,65 @@
/*
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.
Example to List Networks
listOpts := networks.ListOpts{
TenantID: "a99e9b4e620e4db09a2dfb6e42a01e66",
}
allPages, err := networks.List(networkClient, listOpts).AllPages()
if err != nil {
panic(err)
}
allNetworks, err := networks.ExtractNetworks(allPages)
if err != nil {
panic(err)
}
for _, network := range allNetworks {
fmt.Printf("%+v", network)
}
Example to Create a Network
iTrue := true
createOpts := networks.CreateOpts{
Name: "network_1",
AdminStateUp: &iTrue,
}
network, err := networks.Create(networkClient, createOpts).Extract()
if err != nil {
panic(err)
}
Example to Update a Network
networkID := "484cda0e-106f-4f4b-bb3f-d413710bbe78"
updateOpts := networks.UpdateOpts{
Name: "new_name",
}
network, err := networks.Update(networkClient, networkID, updateOpts).Extract()
if err != nil {
panic(err)
}
Example to Delete a Network
networkID := "484cda0e-106f-4f4b-bb3f-d413710bbe78"
err := networks.Delete(networkClient, networkID).ExtractErr()
if err != nil {
panic(err)
}
*/
package networks

View File

@ -0,0 +1,165 @@
package networks
import (
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
// 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)
return q.String(), err
}
// 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) (r GetResult) {
_, r.Err = c.Get(getURL(c, id), &r.Body, nil)
return
}
// CreateOptsBuilder allows extensions to add additional parameters to the
// Create request.
type CreateOptsBuilder interface {
ToNetworkCreateMap() (map[string]interface{}, error)
}
// CreateOpts represents options used to create a network.
type CreateOpts struct {
AdminStateUp *bool `json:"admin_state_up,omitempty"`
Name string `json:"name,omitempty"`
Shared *bool `json:"shared,omitempty"`
TenantID string `json:"tenant_id,omitempty"`
}
// ToNetworkCreateMap builds a request body from CreateOpts.
func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
return gophercloud.BuildRequestBody(opts, "network")
}
// 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) (r CreateResult) {
b, err := opts.ToNetworkCreateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = c.Post(createURL(c), b, &r.Body, nil)
return
}
// UpdateOptsBuilder allows extensions to add additional parameters to the
// Update request.
type UpdateOptsBuilder interface {
ToNetworkUpdateMap() (map[string]interface{}, error)
}
// UpdateOpts represents options used to update a network.
type UpdateOpts struct {
AdminStateUp *bool `json:"admin_state_up,omitempty"`
Name string `json:"name,omitempty"`
Shared *bool `json:"shared,omitempty"`
}
// ToNetworkUpdateMap builds a request body from UpdateOpts.
func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) {
return gophercloud.BuildRequestBody(opts, "network")
}
// 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) (r UpdateResult) {
b, err := opts.ToNetworkUpdateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200, 201},
})
return
}
// Delete accepts a unique ID and deletes the network associated with it.
func Delete(c *gophercloud.ServiceClient, networkID string) (r DeleteResult) {
_, r.Err = c.Delete(deleteURL(c, networkID), nil)
return
}
// IDFromName is a convenience function that returns a network's ID, given
// its name.
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
count := 0
id := ""
pages, err := List(client, nil).AllPages()
if err != nil {
return "", err
}
all, err := ExtractNetworks(pages)
if err != nil {
return "", err
}
for _, s := range all {
if s.Name == name {
count++
id = s.ID
}
}
switch count {
case 0:
return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "network"}
case 1:
return id, nil
default:
return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "network"}
}
}

View File

@ -0,0 +1,111 @@
package networks
import (
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/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) {
var s Network
err := r.ExtractInto(&s)
return &s, err
}
func (r commonResult) ExtractInto(v interface{}) error {
return r.Result.ExtractIntoStructPtr(v, "network")
}
// CreateResult represents the result of a create operation. Call its Extract
// method to interpret it as a Network.
type CreateResult struct {
commonResult
}
// GetResult represents the result of a get operation. Call its Extract
// method to interpret it as a Network.
type GetResult struct {
commonResult
}
// UpdateResult represents the result of an update operation. Call its Extract
// method to interpret it as a Network.
type UpdateResult struct {
commonResult
}
// DeleteResult represents the result of a delete operation. Call its
// ExtractErr method to determine if the request succeeded or failed.
type DeleteResult struct {
gophercloud.ErrResult
}
// Network represents, well, a network.
type Network struct {
// UUID for the network
ID string `json:"id"`
// Human-readable name for the network. Might not be unique.
Name string `json:"name"`
// The administrative state of network. If false (down), the network does not
// forward packets.
AdminStateUp bool `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 `json:"status"`
// Subnets associated with this network.
Subnets []string `json:"subnets"`
// Owner of network.
TenantID string `json:"tenant_id"`
// Specifies whether the network resource can be accessed by any tenant.
Shared bool `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 (r NetworkPage) NextPageURL() (string, error) {
var s struct {
Links []gophercloud.Link `json:"networks_links"`
}
err := r.ExtractInto(&s)
if err != nil {
return "", err
}
return gophercloud.ExtractNextURL(s.Links)
}
// IsEmpty checks whether a NetworkPage struct is empty.
func (r NetworkPage) IsEmpty() (bool, error) {
is, err := ExtractNetworks(r)
return len(is) == 0, err
}
// 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(r pagination.Page) ([]Network, error) {
var s []Network
err := ExtractNetworksInto(r, &s)
return s, err
}
func ExtractNetworksInto(r pagination.Page, v interface{}) error {
return r.(NetworkPage).Result.ExtractIntoSlicePtr(v, "networks")
}

View File

@ -0,0 +1,31 @@
package networks
import "github.com/gophercloud/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)
}