Skip to content
This repository has been archived by the owner on Aug 1, 2023. It is now read-only.

Commit

Permalink
Merge pull request #553 from jtopjian/subnet-no-gateway
Browse files Browse the repository at this point in the history
[rfr] Allow subnets to have no gateway
  • Loading branch information
jrperritt committed Apr 7, 2016
2 parents 1270499 + 2524d11 commit a09b5b4
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 5 deletions.
59 changes: 57 additions & 2 deletions acceptance/openstack/networking/v2/subnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
th "github.com/rackspace/gophercloud/testhelper"
)

func TestList(t *testing.T) {
func TestSubnetList(t *testing.T) {
Setup(t)
defer Teardown()

Expand All @@ -32,7 +32,7 @@ func TestList(t *testing.T) {
th.CheckNoErr(t, err)
}

func TestCRUD(t *testing.T) {
func TestSubnetCRUD(t *testing.T) {
Setup(t)
defer Teardown()

Expand Down Expand Up @@ -61,6 +61,7 @@ func TestCRUD(t *testing.T) {
th.AssertEquals(t, s.IPVersion, 4)
th.AssertEquals(t, s.Name, "my_subnet")
th.AssertEquals(t, s.EnableDHCP, false)
th.AssertEquals(t, s.GatewayIP, "192.168.199.1")
subnetID := s.ID

// Get subnet
Expand All @@ -79,6 +80,60 @@ func TestCRUD(t *testing.T) {
t.Log("Delete subnet")
res := subnets.Delete(Client, subnetID)
th.AssertNoErr(t, res.Err)

// Create subnet with no gateway
t.Log("Create subnet with no gateway")
opts = subnets.CreateOpts{
NetworkID: networkID,
CIDR: "192.168.199.0/24",
IPVersion: subnets.IPv4,
Name: "my_subnet",
EnableDHCP: &enable,
NoGateway: true,
}
s, err = subnets.Create(Client, opts).Extract()
th.AssertNoErr(t, err)

th.AssertEquals(t, s.NetworkID, networkID)
th.AssertEquals(t, s.CIDR, "192.168.199.0/24")
th.AssertEquals(t, s.IPVersion, 4)
th.AssertEquals(t, s.Name, "my_subnet")
th.AssertEquals(t, s.EnableDHCP, false)
th.AssertEquals(t, s.GatewayIP, "")
subnetID = s.ID

// Get subnet
t.Log("Getting subnet with no gateway")
s, err = subnets.Get(Client, subnetID).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, s.ID, subnetID)

// Update subnet
t.Log("Update subnet with no gateway")
s, err = subnets.Update(Client, subnetID, subnets.UpdateOpts{Name: "new_subnet_name"}).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, s.Name, "new_subnet_name")

// Delete subnet
t.Log("Delete subnet with no gateway")
res = subnets.Delete(Client, subnetID)
th.AssertNoErr(t, res.Err)

// Create subnet with invalid gateway configuration
t.Log("Create subnet with invalid gateway configuration")
opts = subnets.CreateOpts{
NetworkID: networkID,
CIDR: "192.168.199.0/24",
IPVersion: subnets.IPv4,
Name: "my_subnet",
EnableDHCP: &enable,
NoGateway: true,
GatewayIP: "192.168.199.1",
}
_, err = subnets.Create(Client, opts).Extract()
if err == nil {
t.Fatalf("Expected an error, got none")
}
}

func TestBatchCreate(t *testing.T) {
Expand Down
7 changes: 4 additions & 3 deletions openstack/networking/v2/subnets/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ func err(str string) error {
}

var (
errNetworkIDRequired = err("A network ID is required")
errCIDRRequired = err("A valid CIDR is required")
errInvalidIPType = err("An IP type must either be 4 or 6")
errNetworkIDRequired = err("A network ID is required")
errCIDRRequired = err("A valid CIDR is required")
errInvalidIPType = err("An IP type must either be 4 or 6")
errInvalidGatewayConfig = err("Both disabling the gateway and specifying a gateway is not allowed")
)
16 changes: 16 additions & 0 deletions openstack/networking/v2/subnets/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ type CreateOpts struct {
TenantID string
AllocationPools []AllocationPool
GatewayIP string
NoGateway bool
IPVersion int
EnableDHCP *bool
DNSNameservers []string
Expand All @@ -128,6 +129,11 @@ func (opts CreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
return nil, errInvalidIPType
}

// Both GatewayIP and NoGateway should not be set
if opts.GatewayIP != "" && opts.NoGateway {
return nil, errInvalidGatewayConfig
}

s["network_id"] = opts.NetworkID
s["cidr"] = opts.CIDR

Expand All @@ -139,6 +145,8 @@ func (opts CreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
}
if opts.GatewayIP != "" {
s["gateway_ip"] = opts.GatewayIP
} else if opts.NoGateway {
s["gateway_ip"] = nil
}
if opts.TenantID != "" {
s["tenant_id"] = opts.TenantID
Expand Down Expand Up @@ -184,6 +192,7 @@ type UpdateOptsBuilder interface {
type UpdateOpts struct {
Name string
GatewayIP string
NoGateway bool
DNSNameservers []string
HostRoutes []HostRoute
EnableDHCP *bool
Expand All @@ -193,6 +202,11 @@ type UpdateOpts struct {
func (opts UpdateOpts) ToSubnetUpdateMap() (map[string]interface{}, error) {
s := make(map[string]interface{})

// Both GatewayIP and NoGateway should not be set
if opts.GatewayIP != "" && opts.NoGateway {
return nil, errInvalidGatewayConfig
}

if opts.EnableDHCP != nil {
s["enable_dhcp"] = &opts.EnableDHCP
}
Expand All @@ -201,6 +215,8 @@ func (opts UpdateOpts) ToSubnetUpdateMap() (map[string]interface{}, error) {
}
if opts.GatewayIP != "" {
s["gateway_ip"] = opts.GatewayIP
} else if opts.NoGateway {
s["gateway_ip"] = nil
}
if opts.DNSNameservers != nil {
s["dns_nameservers"] = opts.DNSNameservers
Expand Down
175 changes: 175 additions & 0 deletions openstack/networking/v2/subnets/requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,24 @@ func TestList(t *testing.T) {
"gateway_ip": "192.0.0.1",
"cidr": "192.0.0.0/8",
"id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
},
{
"name": "my_gatewayless_subnet",
"enable_dhcp": true,
"network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23",
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
"dns_nameservers": [],
"allocation_pools": [
{
"start": "192.168.1.2",
"end": "192.168.1.254"
}
],
"host_routes": [],
"ip_version": 4,
"gateway_ip": null,
"cidr": "192.168.1.0/24",
"id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c"
}
]
}
Expand Down Expand Up @@ -112,6 +130,24 @@ func TestList(t *testing.T) {
CIDR: "192.0.0.0/8",
ID: "54d6f61d-db07-451c-9ab3-b9609b6b6f0b",
},
Subnet{
Name: "my_gatewayless_subnet",
EnableDHCP: true,
NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23",
TenantID: "4fd44f30292945e481c7b8a0c8908869",
DNSNameservers: []string{},
AllocationPools: []AllocationPool{
AllocationPool{
Start: "192.168.1.2",
End: "192.168.1.254",
},
},
HostRoutes: []HostRoute{},
IPVersion: 4,
GatewayIP: "",
CIDR: "192.168.1.0/24",
ID: "54d6f61d-db07-451c-9ab3-b9609b6b6f0c",
},
}

th.CheckDeepEquals(t, expected, actual)
Expand Down Expand Up @@ -270,6 +306,145 @@ func TestCreate(t *testing.T) {
th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126")
}

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

th.Mux.HandleFunc("/v2.0/subnets", 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, `
{
"subnet": {
"network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23",
"ip_version": 4,
"cidr": "192.168.1.0/24",
"gateway_ip": null,
"allocation_pools": [
{
"start": "192.168.1.2",
"end": "192.168.1.254"
}
]
}
}
`)

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

fmt.Fprintf(w, `
{
"subnet": {
"name": "",
"enable_dhcp": true,
"network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23",
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
"allocation_pools": [
{
"start": "192.168.1.2",
"end": "192.168.1.254"
}
],
"host_routes": [],
"ip_version": 4,
"gateway_ip": null,
"cidr": "192.168.1.0/24",
"id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c"
}
}
`)
})

opts := CreateOpts{
NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23",
IPVersion: 4,
CIDR: "192.168.1.0/24",
NoGateway: true,
AllocationPools: []AllocationPool{
AllocationPool{
Start: "192.168.1.2",
End: "192.168.1.254",
},
},
DNSNameservers: []string{},
}
s, err := Create(fake.ServiceClient(), opts).Extract()
th.AssertNoErr(t, err)

th.AssertEquals(t, s.Name, "")
th.AssertEquals(t, s.EnableDHCP, true)
th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a23")
th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869")
th.AssertDeepEquals(t, s.AllocationPools, []AllocationPool{
AllocationPool{
Start: "192.168.1.2",
End: "192.168.1.254",
},
})
th.AssertDeepEquals(t, s.HostRoutes, []HostRoute{})
th.AssertEquals(t, s.IPVersion, 4)
th.AssertEquals(t, s.GatewayIP, "")
th.AssertEquals(t, s.CIDR, "192.168.1.0/24")
th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0c")
}

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

th.Mux.HandleFunc("/v2.0/subnets", 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, `
{
"subnet": {
"network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23",
"ip_version": 4,
"cidr": "192.168.1.0/24",
"gateway_ip": "192.168.1.1",
"allocation_pools": [
{
"start": "192.168.1.2",
"end": "192.168.1.254"
}
]
}
}
`)

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

opts := CreateOpts{
NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23",
IPVersion: 4,
CIDR: "192.168.1.0/24",
NoGateway: true,
GatewayIP: "192.168.1.1",
AllocationPools: []AllocationPool{
AllocationPool{
Start: "192.168.1.2",
End: "192.168.1.254",
},
},
DNSNameservers: []string{},
}
_, err := Create(fake.ServiceClient(), opts).Extract()
if err == nil {
t.Fatalf("Expected an error, got none")
}

if err != errInvalidGatewayConfig {
t.Fatalf("Exected errInvalidGateway but got: %s", err)
}
}

func TestRequiredCreateOpts(t *testing.T) {
res := Create(fake.ServiceClient(), CreateOpts{})
if res.Err == nil {
Expand Down

0 comments on commit a09b5b4

Please sign in to comment.