Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vpnaas: Create VPN service #760

Merged
merged 23 commits into from
Feb 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// +build acceptance networking fwaas

package vpnaas

import (
"testing"

"github.com/gophercloud/gophercloud/acceptance/clients"
layer3 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3"
"github.com/gophercloud/gophercloud/acceptance/tools"
)

func TestServiceCRUD(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}

router, err := layer3.CreateExternalRouter(t, client)
if err != nil {
t.Fatalf("Unable to create router: %v", err)
}
defer layer3.DeleteRouter(t, client, router.ID)

service, err := CreateService(t, client, router.ID)
if err != nil {
t.Fatalf("Unable to create service: %v", err)
}

tools.PrintResource(t, service)
}
32 changes: 32 additions & 0 deletions acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package vpnaas

import (
"testing"

"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services"
)

// CreateService will create a Service with a random name and a specified router ID
// An error will be returned if the service could not be created.
func CreateService(t *testing.T, client *gophercloud.ServiceClient, routerID string) (*services.Service, error) {
serviceName := tools.RandomString("TESTACC-", 8)

t.Logf("Attempting to create service %s", serviceName)

iTrue := true
createOpts := services.CreateOpts{
Name: serviceName,
AdminStateUp: &iTrue,
RouterID: routerID,
}
service, err := services.Create(client, createOpts).Extract()
if err != nil {
return service, err
}

t.Logf("Successfully created service %s", serviceName)

return service, nil
}
21 changes: 21 additions & 0 deletions openstack/networking/v2/extensions/vpnaas/services/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
Package services allows management and retrieval of VPN services in the
OpenStack Networking Service.


Example to Create a Service

createOpts := services.CreateOpts{
Name: "vpnservice1",
Description: "A service",
RouterID: "2512e759-e8d7-4eea-a0af-4a85927a2e59",
AdminStateUp: gophercloud.Enabled,
}

service, err := services.Create(networkClient, createOpts).Extract()
if err != nil {
panic(err)
}

*/
package services
52 changes: 52 additions & 0 deletions openstack/networking/v2/extensions/vpnaas/services/requests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package services

import "github.com/gophercloud/gophercloud"

// CreateOptsBuilder allows extensions to add additional parameters to the
// Create request.
type CreateOptsBuilder interface {
ToServiceCreateMap() (map[string]interface{}, error)
}

// CreateOpts contains all the values needed to create a new VPN service
type CreateOpts struct {
// TenantID specifies a tenant to own the VPN service. The caller must have
// an admin role in order to set this. Otherwise, this field is left unset
// and the caller will be the owner.
TenantID string `json:"tenant_id,omitempty"`

// SubnetID is the ID of the subnet.
SubnetID string `json:"subnet_id,omitempty"`

// RouterID is the ID of the router.
RouterID string `json:"router_id" required:"true"`

// Description is the human readable description of the service.
Description string `json:"description,omitempty"`

// AdminStateUp is the administrative state of the resource, which is up (true) or down (false).
AdminStateUp *bool `json:"admin_state_up"`

// Name is the human readable name of the service.
Name string `json:"name,omitempty"`

// The ID of the flavor.
FlavorID string `json:"flavor_id,omitempty"`
}

// ToServiceCreateMap casts a CreateOpts struct to a map.
func (opts CreateOpts) ToServiceCreateMap() (map[string]interface{}, error) {
return gophercloud.BuildRequestBody(opts, "vpnservice")
}

// Create accepts a CreateOpts struct and uses the values to create a new
// VPN service.
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
b, err := opts.ToServiceCreateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
return
}
62 changes: 62 additions & 0 deletions openstack/networking/v2/extensions/vpnaas/services/results.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package services

import (
"github.com/gophercloud/gophercloud"
)

// Service is a VPN Service
type Service struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doc nit: these fields should have comments to describe what they are.

// TenantID is the ID of the project.
TenantID string `json:"tenant_id"`

// SubnetID is the ID of the subnet.
SubnetID string `json:"subnet_id"`

// RouterID is the ID of the router.
RouterID string `json:"router_id"`

// Description is a human-readable description for the resource.
// Default is an empty string
Description string `json:"description"`

// AdminStateUp is the administrative state of the resource, which is up (true) or down (false).
AdminStateUp bool `json:"admin_state_up"`

// Name is the human readable name of the service.
Name string `json:"name"`

// Status indicates whether IPsec VPN service is currently operational.
// Values are ACTIVE, DOWN, BUILD, ERROR, PENDING_CREATE, PENDING_UPDATE, or PENDING_DELETE.
Status string `json:"status"`

// ID is the unique ID of the VPN service.
ID string `json:"id"`

// ExternalV6IP is the read-only external (public) IPv6 address that is used for the VPN service.
ExternalV6IP string `json:"external_v6_ip"`

// ExternalV4IP is the read-only external (public) IPv4 address that is used for the VPN service.
ExternalV4IP string `json:"external_v4_ip"`

// FlavorID is the ID of the flavor.
FlavorID string `json:"flavor_id"`
}

type commonResult struct {
gophercloud.Result
}

// Extract is a function that accepts a result and extracts a VPN service.
func (r commonResult) Extract() (*Service, error) {
var s struct {
Service *Service `json:"vpnservice"`
}
err := r.ExtractInto(&s)
return s.Service, err
}

// CreateResult represents the result of a create operation. Call its Extract
// method to interpret it as a Service.
type CreateResult struct {
commonResult
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package testing

import (
"fmt"
"net/http"
"testing"

"github.com/gophercloud/gophercloud"
fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services"
th "github.com/gophercloud/gophercloud/testhelper"
)

func TestCreate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()

th.Mux.HandleFunc("/v2.0/vpn/vpnservices", 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, `
{
"vpnservice": {
"router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa",
"name": "vpn",
"admin_state_up": true,
"description": "OpenStack VPN service",
"tenant_id": "10039663455a446d8ba2cbb058b0f578"
}
} `)

w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)

fmt.Fprintf(w, `
{
"vpnservice": {
"router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa",
"status": "PENDING_CREATE",
"name": "vpn",
"external_v6_ip": "2001:db8::1",
"admin_state_up": true,
"subnet_id": null,
"tenant_id": "10039663455a446d8ba2cbb058b0f578",
"external_v4_ip": "172.32.1.11",
"id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828",
"description": "OpenStack VPN service"
}
}
`)
})

options := services.CreateOpts{
TenantID: "10039663455a446d8ba2cbb058b0f578",
Name: "vpn",
Description: "OpenStack VPN service",
AdminStateUp: gophercloud.Enabled,
RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa",
}
actual, err := services.Create(fake.ServiceClient(), options).Extract()
th.AssertNoErr(t, err)
expected := services.Service{
RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa",
Status: "PENDING_CREATE",
Name: "vpn",
ExternalV6IP: "2001:db8::1",
AdminStateUp: true,
SubnetID: "",
TenantID: "10039663455a446d8ba2cbb058b0f578",
ExternalV4IP: "172.32.1.11",
ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828",
Description: "OpenStack VPN service",
}
th.AssertDeepEquals(t, expected, *actual)
}
16 changes: 16 additions & 0 deletions openstack/networking/v2/extensions/vpnaas/services/urls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package services

import "github.com/gophercloud/gophercloud"

const (
rootPath = "vpn"
resourcePath = "vpnservices"
)

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)
}