Skip to content

Commit

Permalink
Vpnaas: Create IPSec site connection (#810)
Browse files Browse the repository at this point in the history
* Added file structure, methods and structs for IPSec site connection creation

* Added unit test

* Added acceptance test

* Got acceptance test to work by adding link between subnet and router

* Added documentation

* removed print statement

* renamed AuthenticationMode to AuthMode to match json string, deleted '(Deprecated)' in comment

* Removed AuthMode and RouteMode from request

* fixed typos
  • Loading branch information
simonre authored and jtopjian committed Mar 8, 2018
1 parent c3bc092 commit 24d38e2
Show file tree
Hide file tree
Showing 7 changed files with 544 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// +build acceptance networking vpnaas

package vpnaas

import (
"testing"

"github.com/gophercloud/gophercloud/acceptance/clients"
networks "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
layer3 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"

"github.com/gophercloud/gophercloud/acceptance/tools"
)

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

// Create Network
network, err := networks.CreateNetwork(t, client)
if err != nil {
t.Fatalf("Unable to create network: %v", err)
}

// Create Subnet
subnet, err := networks.CreateSubnet(t, client, network.ID)
if err != nil {
t.Fatalf("Unable to create subnet: %v", err)
}

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

// Link router and subnet
aiOpts := routers.AddInterfaceOpts{
SubnetID: subnet.ID,
}

_, err = routers.AddInterface(client, router.ID, aiOpts).Extract()
if err != nil {
t.Fatalf("Failed to add interface to router: %v", err)
}

// Create all needed resources for the connection
service, err := CreateService(t, client, router.ID)
if err != nil {
t.Fatalf("Unable to create service: %v", err)
}

ikepolicy, err := CreateIKEPolicy(t, client)
if err != nil {
t.Fatalf("Unable to create IKE policy: %v", err)
}

ipsecpolicy, err := CreateIPSecPolicy(t, client)
if err != nil {
t.Fatalf("Unable to create IPSec Policy: %v", err)
}

peerEPGroup, err := CreateEndpointGroup(t, client)
if err != nil {
t.Fatalf("Unable to create Endpoint Group with CIDR endpoints: %v", err)
}

localEPGroup, err := CreateEndpointGroupWithSubnet(t, client, subnet.ID)
if err != nil {
t.Fatalf("Unable to create Endpoint Group with subnet endpoints: %v", err)
}

conn, err := CreateSiteConnection(t, client, ikepolicy.ID, ipsecpolicy.ID, service.ID, peerEPGroup.ID, localEPGroup.ID)
if err != nil {
t.Fatalf("Unable to create IPSec Site Connection: %v", err)
}

tools.PrintResource(t, conn)

}
60 changes: 59 additions & 1 deletion acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections"
)

// CreateService will create a Service with a random name and a specified router ID
Expand Down Expand Up @@ -53,7 +54,7 @@ func DeleteService(t *testing.T, client *gophercloud.ServiceClient, serviceID st
func CreateIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ipsecpolicies.Policy, error) {
policyName := tools.RandomString("TESTACC-", 8)

t.Logf("Attempting to create policy %s", policyName)
t.Logf("Attempting to create IPSec policy %s", policyName)

createOpts := ipsecpolicies.CreateOpts{
Name: policyName,
Expand Down Expand Up @@ -157,4 +158,61 @@ func DeleteEndpointGroup(t *testing.T, client *gophercloud.ServiceClient, epGrou
}

t.Logf("Deleted endpoint group: %s", epGroupID)

}

// CreateEndpointGroupWithSubnet will create an endpoint group with a random name.
// An error will be returned if the group could not be created.
func CreateEndpointGroupWithSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*endpointgroups.EndpointGroup, error) {
groupName := tools.RandomString("TESTACC-", 8)

t.Logf("Attempting to create group %s", groupName)

createOpts := endpointgroups.CreateOpts{
Name: groupName,
Type: endpointgroups.TypeSubnet,
Endpoints: []string{
subnetID,
},
}
group, err := endpointgroups.Create(client, createOpts).Extract()
if err != nil {
return group, err
}

t.Logf("Successfully created group %s", groupName)

return group, nil
}

// CreateSiteConnection will create an IPSec site connection with a random name and specified
// IKE policy, IPSec policy, service, peer EP group and local EP Group.
// An error will be returned if the connection could not be created.
func CreateSiteConnection(t *testing.T, client *gophercloud.ServiceClient, ikepolicyID string, ipsecpolicyID string, serviceID string, peerEPGroupID string, localEPGroupID string) (*siteconnections.Connection, error) {
connectionName := tools.RandomString("TESTACC-", 8)

t.Logf("Attempting to create IPSec site connection %s", connectionName)

createOpts := siteconnections.CreateOpts{
Name: connectionName,
PSK: "secret",
Initiator: siteconnections.InitiatorBiDirectional,
AdminStateUp: gophercloud.Enabled,
IPSecPolicyID: ipsecpolicyID,
PeerEPGroupID: peerEPGroupID,
IKEPolicyID: ikepolicyID,
VPNServiceID: serviceID,
LocalEPGroupID: localEPGroupID,
PeerAddress: "172.24.4.233",
PeerID: "172.24.4.233",
MTU: 1500,
}
connection, err := siteconnections.Create(client, createOpts).Extract()
if err != nil {
return connection, err
}

t.Logf("Successfully created IPSec Site Connection %s", connectionName)

return connection, nil
}
27 changes: 27 additions & 0 deletions openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Package siteconnections allows management and retrieval of IPSec site connections in the
OpenStack Networking Service.
Example to create an IPSec site connection
createOpts := siteconnections.CreateOpts{
Name: "Connection1",
PSK: "secret",
Initiator: siteconnections.InitiatorBiDirectional,
AdminStateUp: gophercloud.Enabled,
IPSecPolicyID: "4ab0a72e-64ef-4809-be43-c3f7e0e5239b",
PeerEPGroupID: "5f5801b1-b383-4cf0-bf61-9e85d4044b2d",
IKEPolicyID: "47a880f9-1da9-468c-b289-219c9eca78f0",
VPNServiceID: "692c1ec8-a7cd-44d9-972b-8ed3fe4cc476",
LocalEPGroupID: "498bb96a-1517-47ea-b1eb-c4a53db46a16",
PeerAddress: "172.24.4.233",
PeerID: "172.24.4.233",
MTU: 1500,
}
connection, err := siteconnections.Create(client, createOpts).Extract()
if err != nil {
panic(err)
}
*/
package siteconnections
129 changes: 129 additions & 0 deletions openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package siteconnections

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

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

const (
ActionHold Action = "hold"
ActionClear Action = "clear"
ActionRestart Action = "restart"
ActionDisabled Action = "disabled"
ActionRestartByPeer Action = "restart-by-peer"
InitiatorBiDirectional Initiator = "bi-directional"
InitiatorResponseOnly Initiator = "response-only"
)

// DPDCreateOpts contains all the values needed to create a valid configuration for Dead Peer detection protocols
type DPDCreateOpts struct {
// The dead peer detection (DPD) action.
// A valid value is clear, hold, restart, disabled, or restart-by-peer.
// Default value is hold.
Action Action `json:"action,omitempty"`

// The dead peer detection (DPD) timeout in seconds.
// A valid value is a positive integer that is greater than the DPD interval value.
// Default is 120.
Timeout int `json:"timeout,omitempty"`

// The dead peer detection (DPD) interval, in seconds.
// A valid value is a positive integer.
// Default is 30.
Interval int `json:"interval,omitempty"`
}

// CreateOpts contains all the values needed to create a new IPSec site connection
type CreateOpts struct {
// The ID of the IKE policy
IKEPolicyID string `json:"ikepolicy_id"`

// The ID of the VPN Service
VPNServiceID string `json:"vpnservice_id"`

// The ID for the endpoint group that contains private subnets for the local side of the connection.
// You must specify this parameter with the peer_ep_group_id parameter unless
// in backward- compatible mode where peer_cidrs is provided with a subnet_id for the VPN service.
LocalEPGroupID string `json:"local_ep_group_id,omitempty"`

// The ID of the IPsec policy.
IPSecPolicyID string `json:"ipsecpolicy_id"`

// The peer router identity for authentication.
// A valid value is an IPv4 address, IPv6 address, e-mail address, key ID, or FQDN.
// Typically, this value matches the peer_address value.
PeerID string `json:"peer_id"`

// The ID of the project
TenantID string `json:"tenant_id,omitempty"`

// The ID for the endpoint group that contains private CIDRs in the form < net_address > / < prefix >
// for the peer side of the connection.
// You must specify this parameter with the local_ep_group_id parameter unless in backward-compatible mode
// where peer_cidrs is provided with a subnet_id for the VPN service.
PeerEPGroupID string `json:"peer_ep_group_id,omitempty"`

// An ID to be used instead of the external IP address for a virtual router used in traffic between instances on different networks in east-west traffic.
// Most often, local ID would be domain name, email address, etc.
// If this is not configured then the external IP address will be used as the ID.
LocalID string `json:"local_id,omitempty"`

// The human readable name of the connection.
// Does not have to be unique.
// Default is an empty string
Name string `json:"name,omitempty"`

// The human readable description of the connection.
// Does not have to be unique.
// Default is an empty string
Description string `json:"description,omitempty"`

// The peer gateway public IPv4 or IPv6 address or FQDN.
PeerAddress string `json:"peer_address"`

// The pre-shared key.
// A valid value is any string.
PSK string `json:"psk"`

// Indicates whether this VPN can only respond to connections or both respond to and initiate connections.
// A valid value is response-only or bi-directional. Default is bi-directional.
Initiator Initiator `json:"initiator,omitempty"`

// Unique list of valid peer private CIDRs in the form < net_address > / < prefix > .
PeerCIDRs []string `json:"peer_cidrs,omitempty"`

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

// A dictionary with dead peer detection (DPD) protocol controls.
DPD *DPDCreateOpts `json:"dpd,omitempty"`

// The maximum transmission unit (MTU) value to address fragmentation.
// Minimum value is 68 for IPv4, and 1280 for IPv6.
MTU int `json:"mtu,omitempty"`
}

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

// Create accepts a CreateOpts struct and uses the values to create a new
// IPSec site connection.
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
b, err := opts.ToConnectionCreateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
return
}
Loading

0 comments on commit 24d38e2

Please sign in to comment.