Add update and delete subnet operations :cool:
diff --git a/openstack/networking/v2/subnets/errors.go b/openstack/networking/v2/subnets/errors.go
index 9299927..ca2df72 100644
--- a/openstack/networking/v2/subnets/errors.go
+++ b/openstack/networking/v2/subnets/errors.go
@@ -7,7 +7,9 @@
 }
 
 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")
+	ErrCIDRNotUpdatable      = err("CIDR attributes cannot be updated")
+	ErrIPVersionNotUpdatable = err("IP Version attributes cannot be updated")
 )
diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go
index 93641d9..f10bf18 100644
--- a/openstack/networking/v2/subnets/requests.go
+++ b/openstack/networking/v2/subnets/requests.go
@@ -124,16 +124,15 @@
 	}
 
 	reqBody := request{Subnet: subnet{
-		NetworkID: opts.NetworkID,
-		CIDR:      opts.CIDR,
+		NetworkID:  opts.NetworkID,
+		CIDR:       opts.CIDR,
+		Name:       maybeString(opts.Name),
+		TenantID:   maybeString(opts.TenantID),
+		GatewayIP:  maybeString(opts.GatewayIP),
+		ID:         maybeString(opts.ID),
+		EnableDHCP: opts.EnableDHCP,
 	}}
 
-	reqBody.Subnet.Name = maybeString(opts.Name)
-	reqBody.Subnet.TenantID = maybeString(opts.TenantID)
-	reqBody.Subnet.GatewayIP = maybeString(opts.GatewayIP)
-	reqBody.Subnet.ID = maybeString(opts.ID)
-	reqBody.Subnet.EnableDHCP = opts.EnableDHCP
-
 	if opts.IPVersion != 0 {
 		reqBody.Subnet.IPVersion = opts.IPVersion
 	}
@@ -159,3 +158,63 @@
 
 	return res.Subnet, nil
 }
+
+func Update(c *gophercloud.ServiceClient, id string, opts SubnetOpts) (*Subnet, error) {
+	if opts.CIDR != "" {
+		return nil, ErrCIDRNotUpdatable
+	}
+	if opts.IPVersion != 0 {
+		return nil, ErrIPVersionNotUpdatable
+	}
+
+	type subnet struct {
+		NetworkID       string           `json:"network_id,omitempty"`
+		Name            *string          `json:"name,omitempty"`
+		TenantID        *string          `json:"tenant_id,omitempty"`
+		AllocationPools []AllocationPool `json:"allocation_pools,omitempty"`
+		GatewayIP       *string          `json:"gateway_ip,omitempty"`
+		ID              *string          `json:"id,omitempty"`
+		EnableDHCP      *bool            `json:"enable_dhcp,omitempty"`
+	}
+	type request struct {
+		Subnet subnet `json:"subnet"`
+	}
+
+	reqBody := request{Subnet: subnet{
+		NetworkID:  opts.NetworkID,
+		Name:       maybeString(opts.Name),
+		TenantID:   maybeString(opts.TenantID),
+		GatewayIP:  maybeString(opts.GatewayIP),
+		ID:         maybeString(opts.ID),
+		EnableDHCP: opts.EnableDHCP,
+	}}
+
+	if len(opts.AllocationPools) != 0 {
+		reqBody.Subnet.AllocationPools = opts.AllocationPools
+	}
+
+	type response struct {
+		Subnet *Subnet `json:"subnet"`
+	}
+
+	var res response
+	_, err := perigee.Request("PUT", UpdateURL(c, id), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		ReqBody:     &reqBody,
+		Results:     &res,
+		OkCodes:     []int{201},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return res.Subnet, nil
+}
+
+func Delete(c *gophercloud.ServiceClient, id string) error {
+	_, err := perigee.Request("DELETE", DeleteURL(c, id), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{204},
+	})
+	return err
+}
diff --git a/openstack/networking/v2/subnets/requests_test.go b/openstack/networking/v2/subnets/requests_test.go
index f5716cf..edf59e7 100644
--- a/openstack/networking/v2/subnets/requests_test.go
+++ b/openstack/networking/v2/subnets/requests_test.go
@@ -258,3 +258,78 @@
 	th.AssertEquals(t, s.CIDR, "192.168.199.0/24")
 	th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126")
 }
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", TokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "subnet": {
+        "name": "my_new_subnet"
+    }
+}
+		`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "subnet": {
+        "name": "my_new_subnet",
+        "enable_dhcp": true,
+        "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+        "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
+        "dns_nameservers": [],
+        "allocation_pools": [
+            {
+                "start": "10.0.0.2",
+                "end": "10.0.0.254"
+            }
+        ],
+        "host_routes": [],
+        "ip_version": 4,
+        "gateway_ip": "10.0.0.1",
+        "cidr": "10.0.0.0/24",
+        "id": "08eae331-0402-425a-923c-34f7cfe39c1b"
+    }
+}
+	`)
+	})
+
+	opts := SubnetOpts{Name: "my_new_subnet"}
+	s, err := Update(ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts)
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, s.Name, "my_new_subnet")
+	th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b")
+}
+
+func TestCertainAttrsCannotBeUpdated(t *testing.T) {
+	opts := SubnetOpts{IPVersion: 6, CIDR: "192.0.0.1/24"}
+	_, err := Update(ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts)
+
+	if err == nil {
+		t.Errorf("An error was expected when updating IPVersion and CIDR, none was raised")
+	}
+}
+
+func TestDelete(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", TokenID)
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	err := Delete(ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b")
+	th.AssertNoErr(t, err)
+}
diff --git a/openstack/networking/v2/subnets/urls.go b/openstack/networking/v2/subnets/urls.go
index 485f97a..fe20c53 100644
--- a/openstack/networking/v2/subnets/urls.go
+++ b/openstack/networking/v2/subnets/urls.go
@@ -23,3 +23,11 @@
 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)
+}
diff --git a/openstack/networking/v2/subnets/urls_tests.go b/openstack/networking/v2/subnets/urls_tests.go
index 336e2fe..eec5275 100644
--- a/openstack/networking/v2/subnets/urls_tests.go
+++ b/openstack/networking/v2/subnets/urls_tests.go
@@ -30,3 +30,15 @@
 	expected := Endpoint + "v2.0/subnets"
 	th.AssertEquals(t, expected, actual)
 }
+
+func TestUpdateURL(t *testing.T) {
+	actual := UpdateURL(EndpointClient(), "foo")
+	expected := Endpoint + "v2.0/subnets/foo"
+	th.AssertEquals(t, expected, actual)
+}
+
+func TestDeleteURL(t *testing.T) {
+	actual := DeleteURL(EndpointClient(), "foo")
+	expected := Endpoint + "v2.0/subnets/foo"
+	th.AssertEquals(t, expected, actual)
+}