Update Subnet Gateway Behavior (#102)

This commit enables all three behaviors of a gateway during subnet creation and
updating.

If a GatewayIP is omitted, Neutron will provision a default gateway.
If a GatewayIP is set to an empty string, no gateway will be provisioned.
If a GatewayIP is specified, it will be used as the gateway IP.
diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go
index 9432dda..cc5befb 100644
--- a/acceptance/openstack/networking/v2/networking.go
+++ b/acceptance/openstack/networking/v2/networking.go
@@ -72,13 +72,12 @@
 	subnetOctet := tools.RandomInt(1, 250)
 	subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet)
 	subnetGateway := fmt.Sprintf("192.168.%d.1", subnetOctet)
-	iFalse := false
 	createOpts := subnets.CreateOpts{
 		NetworkID:  networkID,
 		CIDR:       subnetCIDR,
 		IPVersion:  4,
 		Name:       subnetName,
-		EnableDHCP: &iFalse,
+		EnableDHCP: gophercloud.Disabled,
 		GatewayIP:  &subnetGateway,
 	}
 
@@ -93,6 +92,68 @@
 	return subnet, nil
 }
 
+// CreateSubnetWithDefaultGateway will create a subnet on the specified Network
+// ID and have Neutron set the gateway by default An error will be returned if
+// the subnet could not be created.
+func CreateSubnetWithDefaultGateway(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) {
+	subnetName := tools.RandomString("TESTACC-", 8)
+	subnetOctet := tools.RandomInt(1, 250)
+	subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet)
+	createOpts := subnets.CreateOpts{
+		NetworkID:  networkID,
+		CIDR:       subnetCIDR,
+		IPVersion:  4,
+		Name:       subnetName,
+		EnableDHCP: gophercloud.Disabled,
+	}
+
+	t.Logf("Attempting to create subnet: %s", subnetName)
+
+	subnet, err := subnets.Create(client, createOpts).Extract()
+	if err != nil {
+		return subnet, err
+	}
+
+	t.Logf("Successfully created subnet.")
+	return subnet, nil
+}
+
+// CreateSubnetWithNoGateway will create a subnet with no gateway on the
+// specified Network ID.  An error will be returned if the subnet could not be
+// created.
+func CreateSubnetWithNoGateway(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) {
+	var noGateway = ""
+	subnetName := tools.RandomString("TESTACC-", 8)
+	subnetOctet := tools.RandomInt(1, 250)
+	subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet)
+	dhcpStart := fmt.Sprintf("192.168.%d.10", subnetOctet)
+	dhcpEnd := fmt.Sprintf("192.168.%d.200", subnetOctet)
+	createOpts := subnets.CreateOpts{
+		NetworkID:  networkID,
+		CIDR:       subnetCIDR,
+		IPVersion:  4,
+		Name:       subnetName,
+		EnableDHCP: gophercloud.Disabled,
+		GatewayIP:  &noGateway,
+		AllocationPools: []subnets.AllocationPool{
+			{
+				Start: dhcpStart,
+				End:   dhcpEnd,
+			},
+		},
+	}
+
+	t.Logf("Attempting to create subnet: %s", subnetName)
+
+	subnet, err := subnets.Create(client, createOpts).Extract()
+	if err != nil {
+		return subnet, err
+	}
+
+	t.Logf("Successfully created subnet.")
+	return subnet, nil
+}
+
 // DeleteNetwork will delete a network with a specified ID. A fatal error will
 // occur if the delete was not successful. This works best when used as a
 // deferred function.
diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go
index 49c970a..1d7696c 100644
--- a/acceptance/openstack/networking/v2/subnets_test.go
+++ b/acceptance/openstack/networking/v2/subnets_test.go
@@ -3,6 +3,8 @@
 package v2
 
 import (
+	"fmt"
+	"strings"
 	"testing"
 
 	"github.com/gophercloud/gophercloud/acceptance/clients"
@@ -71,3 +73,86 @@
 
 	PrintSubnet(t, newSubnet)
 }
+
+func TestSubnetsDefaultGateway(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	// Create Network
+	network, err := CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer DeleteNetwork(t, client, network.ID)
+
+	// Create Subnet
+	subnet, err := CreateSubnetWithDefaultGateway(t, client, network.ID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer DeleteSubnet(t, client, subnet.ID)
+
+	PrintSubnet(t, subnet)
+
+	if subnet.GatewayIP == "" {
+		t.Fatalf("A default gateway was not created.")
+	}
+
+	var noGateway = ""
+	updateOpts := subnets.UpdateOpts{
+		GatewayIP: &noGateway,
+	}
+
+	newSubnet, err := subnets.Update(client, subnet.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update subnet")
+	}
+
+	if newSubnet.GatewayIP != "" {
+		t.Fatalf("Gateway was not updated correctly")
+	}
+}
+
+func TestSubnetsNoGateway(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	// Create Network
+	network, err := CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer DeleteNetwork(t, client, network.ID)
+
+	// Create Subnet
+	subnet, err := CreateSubnetWithNoGateway(t, client, network.ID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer DeleteSubnet(t, client, subnet.ID)
+
+	PrintSubnet(t, subnet)
+
+	if subnet.GatewayIP != "" {
+		t.Fatalf("A gateway exists when it shouldn't.")
+	}
+
+	subnetParts := strings.Split(subnet.CIDR, ".")
+	newGateway := fmt.Sprintf("%s.%s.%s.1", subnetParts[0], subnetParts[1], subnetParts[2])
+	updateOpts := subnets.UpdateOpts{
+		GatewayIP: &newGateway,
+	}
+
+	newSubnet, err := subnets.Update(client, subnet.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update subnet")
+	}
+
+	if newSubnet.GatewayIP == "" {
+		t.Fatalf("Gateway was not updated correctly")
+	}
+}