Add BeginDetaching call for Cinder v2 (#90)

Per Cinder API, os-begin_detaching action needs to
be called prior to os-terminate_connection.
diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go
index d2a2c79..1aff494 100644
--- a/openstack/blockstorage/extensions/volumeactions/requests.go
+++ b/openstack/blockstorage/extensions/volumeactions/requests.go
@@ -50,6 +50,15 @@
 	return
 }
 
+// BeginDetach will mark the volume as detaching
+func BeginDetaching(client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) {
+	b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})}
+	_, r.Err = client.Post(beginDetachingURL(client, id), b, nil, &gophercloud.RequestOpts{
+		OkCodes: []int{202},
+	})
+	return
+}
+
 // DetachOptsBuilder allows extensions to add additional parameters to the
 // Detach request.
 type DetachOptsBuilder interface {
diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go
index f9e8120..b5695b7 100644
--- a/openstack/blockstorage/extensions/volumeactions/results.go
+++ b/openstack/blockstorage/extensions/volumeactions/results.go
@@ -7,6 +7,11 @@
 	gophercloud.ErrResult
 }
 
+// BeginDetachingResult contains the response body and error from a Get request.
+type BeginDetachingResult struct {
+	gophercloud.ErrResult
+}
+
 // DetachResult contains the response body and error from a Get request.
 type DetachResult struct {
 	gophercloud.ErrResult
diff --git a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go
index 34df070..4c3c0dd 100644
--- a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go
+++ b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go
@@ -34,6 +34,26 @@
 		})
 }
 
+func MockBeginDetachingResponse(t *testing.T) {
+	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
+		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, `
+{
+    "os-begin_detaching": {}
+}
+          `)
+
+			w.Header().Add("Content-Type", "application/json")
+			w.WriteHeader(http.StatusAccepted)
+
+			fmt.Fprintf(w, `{}`)
+		})
+}
+
 func MockDetachResponse(t *testing.T) {
 	th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
 		func(w http.ResponseWriter, r *http.Request) {
diff --git a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go
index f47d773..b1f7af7 100644
--- a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go
+++ b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go
@@ -24,6 +24,16 @@
 	th.AssertNoErr(t, err)
 }
 
+func TestBeginDetaching(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockBeginDetachingResponse(t)
+
+	err := volumeactions.BeginDetaching(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr()
+	th.AssertNoErr(t, err)
+}
+
 func TestDetach(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
diff --git a/openstack/blockstorage/extensions/volumeactions/urls.go b/openstack/blockstorage/extensions/volumeactions/urls.go
index a61ee7d..a172549 100644
--- a/openstack/blockstorage/extensions/volumeactions/urls.go
+++ b/openstack/blockstorage/extensions/volumeactions/urls.go
@@ -6,6 +6,10 @@
 	return c.ServiceURL("volumes", id, "action")
 }
 
+func beginDetachingURL(c *gophercloud.ServiceClient, id string) string {
+	return attachURL(c, id)
+}
+
 func detachURL(c *gophercloud.ServiceClient, id string) string {
 	return attachURL(c, id)
 }