feature/shared file systems: share acceptance tests (#128)

* feature/filestorage: acceptance tests

Implements acceptance tests for `sharedfilesystems/v2/shares`

* sfs/acceptance: create share-network in env script

Adds creation of a share-network in the acceptance test environment
setup script, and stores the network id in an environment variable

* sfs: adds ShareNetworkID to AcceptanceTestChoices

Gets ShareNetworkID from the OS environment variable:
`OS_SHARE_NETWORK_ID`

* sfs: removes hard-coding of the share-network-id

* sfs: add OS_SHARE_NETWORK_ID to acceptance README

* sfs/acceptance: move WaitForStatus to acc. tests

Moves the `WaitForStatus`-function from the actual library to the
acceptance test folder and does not export it
diff --git a/acceptance/README.md b/acceptance/README.md
index d969e03..620bdf8 100644
--- a/acceptance/README.md
+++ b/acceptance/README.md
@@ -45,6 +45,11 @@
 |`OS_POOL_NAME`|The Pool from where to obtain Floating IPs|
 |`OS_NETWORK_NAME`|The network to launch instances on|
 
+#### Shared file systems
+|Name|Description|
+|---|---|
+|`OS_SHARE_NETWORK_ID`| The share network ID to use when creating shares|
+
 ### 2. Run the test suite
 
 From the root directory, run:
diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go
index 6658200..f07754f 100644
--- a/acceptance/clients/clients.go
+++ b/acceptance/clients/clients.go
@@ -32,6 +32,9 @@
 
 	// ExternalNetworkID is the network ID of the external network.
 	ExternalNetworkID string
+
+	// ShareNetworkID is the Manila Share network ID
+	ShareNetworkID string
 }
 
 // AcceptanceTestChoicesFromEnv populates a ComputeChoices struct from environment variables.
@@ -43,6 +46,7 @@
 	networkName := os.Getenv("OS_NETWORK_NAME")
 	floatingIPPoolName := os.Getenv("OS_POOL_NAME")
 	externalNetworkID := os.Getenv("OS_EXTGW_ID")
+	shareNetworkID := os.Getenv("OS_SHARE_NETWORK_ID")
 
 	missing := make([]string, 0, 3)
 	if imageID == "" {
@@ -63,7 +67,9 @@
 	if networkName == "" {
 		networkName = "private"
 	}
-
+	if shareNetworkID == "" {
+		missing = append(missing, "OS_SHARE_NETWORK_ID")
+	}
 	notDistinct := ""
 	if flavorID == flavorIDResize {
 		notDistinct = "OS_FLAVOR_ID and OS_FLAVOR_ID_RESIZE must be distinct."
@@ -81,7 +87,15 @@
 		return nil, fmt.Errorf(text)
 	}
 
-	return &AcceptanceTestChoices{ImageID: imageID, FlavorID: flavorID, FlavorIDResize: flavorIDResize, FloatingIPPoolName: floatingIPPoolName, NetworkName: networkName, ExternalNetworkID: externalNetworkID}, nil
+	return &AcceptanceTestChoices{
+		ImageID:            imageID,
+		FlavorID:           flavorID,
+		FlavorIDResize:     flavorIDResize,
+		FloatingIPPoolName: floatingIPPoolName,
+		NetworkName:        networkName,
+		ExternalNetworkID:  externalNetworkID,
+		ShareNetworkID:     shareNetworkID,
+	}, nil
 }
 
 // NewBlockStorageV1Client returns a *ServiceClient for making calls
diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go
new file mode 100644
index 0000000..e9060b4
--- /dev/null
+++ b/acceptance/openstack/sharedfilesystems/v2/shares.go
@@ -0,0 +1,78 @@
+package v2
+
+import (
+	"encoding/json"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares"
+	"testing"
+)
+
+// CreateShare will create a share with a name, and a size of 1Gb. An
+// error will be returned if the share could not be created
+func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share, error) {
+	if testing.Short() {
+		t.Skip("Skipping test that requres share creation in short mode.")
+	}
+
+	choices, err := clients.AcceptanceTestChoicesFromEnv()
+	if err != nil {
+		t.Fatalf("Unable to fetch environment information")
+	}
+
+	t.Logf("Share network id %s", choices.ShareNetworkID)
+	createOpts := shares.CreateOpts{
+		Size:           1,
+		Name:           "My Test Share",
+		ShareProto:     "NFS",
+		ShareNetworkID: choices.ShareNetworkID,
+	}
+
+	share, err := shares.Create(client, createOpts).Extract()
+	if err != nil {
+		return share, err
+	}
+
+	err = waitForStatus(client, share.ID, "available", 60)
+	if err != nil {
+		return share, err
+	}
+
+	return share, nil
+}
+
+// DeleteShare will delete a share. A fatal error will occur if the share
+// failed to be deleted. This works best when used as a deferred function.
+func DeleteShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) {
+	err := shares.Delete(client, share.ID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete share %s: %v", share.ID, err)
+	}
+
+	t.Logf("Deleted share: %s", share.ID)
+}
+
+// PrintShare prints some information of the share
+func PrintShare(t *testing.T, share *shares.Share) {
+	asJSON, err := json.MarshalIndent(share, "", " ")
+	if err != nil {
+		t.Logf("Cannot print the contents of %s", share.ID)
+	}
+
+	t.Logf("Share %s", string(asJSON))
+}
+
+func waitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
+	return gophercloud.WaitFor(secs, func() (bool, error) {
+		current, err := shares.Get(c, id).Extract()
+		if err != nil {
+			return false, err
+		}
+
+		if current.Status == status {
+			return true, nil
+		}
+
+		return false, nil
+	})
+}
diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go
new file mode 100644
index 0000000..ed5d7cc
--- /dev/null
+++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go
@@ -0,0 +1,27 @@
+package v2
+
+import (
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares"
+	"testing"
+)
+
+func TestShareCreate(t *testing.T) {
+	client, err := clients.NewSharedFileSystemV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a sharedfs client: %v", err)
+	}
+
+	share, err := CreateShare(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create a share: %v", err)
+	}
+
+	defer DeleteShare(t, client, share)
+
+	created, err := shares.Get(client, share.ID).Extract()
+	if err != nil {
+		t.Errorf("Unable to retrieve share: %v", err)
+	}
+	PrintShare(t, created)
+}