Feature/filestorage sharenetworks create (#118)

* sfs: Add support for share networks Create

* sfs: Add Manila to acceptance test environment

* sfs: Add acceptance tests for share networks Create

* sfs: Remove unused urls

Some url functions were introduced but they belong to other
PRs. Will be repushed with in the correct PRs

* sfs: Make name and descr required for creating share network

* sfs: Remove required parameters

After taking a close look at the code it appeared that 'name'
and 'description' are not required parameters
diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go
index 86871aa..6658200 100644
--- a/acceptance/clients/clients.go
+++ b/acceptance/clients/clients.go
@@ -122,6 +122,25 @@
 	})
 }
 
+// NewSharedFileSystemV2Client returns a *ServiceClient for making calls
+// to the OpenStack Shared File System v2 API. An error will be returned
+// if authentication or client creation was not possible.
+func NewSharedFileSystemV2Client() (*gophercloud.ServiceClient, error) {
+	ao, err := openstack.AuthOptionsFromEnv()
+	if err != nil {
+		return nil, err
+	}
+
+	client, err := openstack.AuthenticatedClient(ao)
+	if err != nil {
+		return nil, err
+	}
+
+	return openstack.NewSharedFileSystemV2(client, gophercloud.EndpointOpts{
+		Region: os.Getenv("OS_REGION_NAME"),
+	})
+}
+
 // NewComputeV2Client returns a *ServiceClient for making calls
 // to the OpenStack Compute v2 API. An error will be returned
 // if authentication or client creation was not possible.
diff --git a/acceptance/openstack/sharedfilesystems/v2/pkg.go b/acceptance/openstack/sharedfilesystems/v2/pkg.go
new file mode 100644
index 0000000..5a5cd2b
--- /dev/null
+++ b/acceptance/openstack/sharedfilesystems/v2/pkg.go
@@ -0,0 +1,3 @@
+// The v2 package contains acceptance tests for the Openstack Manila V2 service.
+
+package v2
diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go b/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go
new file mode 100644
index 0000000..a964515
--- /dev/null
+++ b/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go
@@ -0,0 +1,49 @@
+package v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharenetworks"
+)
+
+// CreateShareNetwork will create a share network with a random name. An
+// error will be returned if the share network was unable to be created.
+func CreateShareNetwork(t *testing.T, client *gophercloud.ServiceClient) (*sharenetworks.ShareNetwork, error) {
+	if testing.Short() {
+		t.Skip("Skipping test that requires share network creation in short mode.")
+	}
+
+	shareNetworkName := tools.RandomString("ACPTTEST", 16)
+	t.Logf("Attempting to create share network: %s", shareNetworkName)
+
+	createOpts := sharenetworks.CreateOpts{
+		Name:        shareNetworkName,
+		Description: "This is a shared network",
+	}
+
+	shareNetwork, err := sharenetworks.Create(client, createOpts).Extract()
+	if err != nil {
+		return shareNetwork, err
+	}
+
+	return shareNetwork, nil
+}
+
+// PrintShareNetwork will print a share network and all of its attributes.
+func PrintShareNetwork(t *testing.T, sharenetwork *sharenetworks.ShareNetwork) {
+	t.Logf("ID: %s", sharenetwork.ID)
+	t.Logf("Project ID: %s", sharenetwork.ProjectID)
+	t.Logf("Neutron network ID: %s", sharenetwork.NeutronNetID)
+	t.Logf("Neutron sub-network ID: %s", sharenetwork.NeutronSubnetID)
+	t.Logf("Nova network ID: %s", sharenetwork.NovaNetID)
+	t.Logf("Network type: %s", sharenetwork.NetworkType)
+	t.Logf("Segmentation ID: %d", sharenetwork.SegmentationID)
+	t.Logf("CIDR: %s", sharenetwork.CIDR)
+	t.Logf("IP version: %d", sharenetwork.IPVersion)
+	t.Logf("Name: %s", sharenetwork.Name)
+	t.Logf("Description: %s", sharenetwork.Description)
+	t.Logf("Created at: %s", sharenetwork.CreatedAt)
+	t.Logf("Updated at: %s", sharenetwork.UpdatedAt)
+}
diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go
new file mode 100644
index 0000000..6b74007
--- /dev/null
+++ b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go
@@ -0,0 +1,23 @@
+package v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+)
+
+func TestShareNetworkCreate(t *testing.T) {
+	client, err := clients.NewSharedFileSystemV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create shared file system client: %v", err)
+	}
+
+	shareNetwork, err := CreateShareNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create share network: %v", err)
+	}
+
+	// TODO: delete the share network when the delete is implemented
+
+	PrintShareNetwork(t, shareNetwork)
+}
diff --git a/openstack/client.go b/openstack/client.go
index a45b538..3664cbc 100644
--- a/openstack/client.go
+++ b/openstack/client.go
@@ -269,6 +269,16 @@
 	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
 }
 
+// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service.
+func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
+	eo.ApplyDefaults("sharev2")
+	url, err := client.EndpointLocator(eo)
+	if err != nil {
+		return nil, err
+	}
+	return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
+}
+
 // NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
 // CDN service.
 func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
diff --git a/openstack/sharedfilesystems/v2/sharenetworks/requests.go b/openstack/sharedfilesystems/v2/sharenetworks/requests.go
new file mode 100644
index 0000000..f551861
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/sharenetworks/requests.go
@@ -0,0 +1,46 @@
+package sharenetworks
+
+import "github.com/gophercloud/gophercloud"
+
+// CreateOptsBuilder allows extensions to add additional parameters to the
+// Create request.
+type CreateOptsBuilder interface {
+	ToShareNetworkCreateMap() (map[string]interface{}, error)
+}
+
+// CreateOpts contains options for creating a ShareNetwork. This object is
+// passed to the sharenetworks.Create function. For more information about
+// these parameters, see the ShareNetwork object.
+type CreateOpts struct {
+	// The UUID of the Neutron network to set up for share servers
+	NeutronNetID string `json:"neutron_net_id,omitempty"`
+	// The UUID of the Neutron subnet to set up for share servers
+	NeutronSubnetID string `json:"neutron_subnet_id,omitempty"`
+	// The UUID of the nova network to set up for share servers
+	NovaNetID string `json:"nova_net_id,omitempty"`
+	// The share network name
+	Name string `json:"name"`
+	// The share network description
+	Description string `json:"description"`
+}
+
+// ToShareNetworkCreateMap assembles a request body based on the contents of a
+// CreateOpts.
+func (opts CreateOpts) ToShareNetworkCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "share_network")
+}
+
+// Create will create a new ShareNetwork based on the values in CreateOpts. To
+// extract the ShareNetwork object from the response, call the Extract method
+// on the CreateResult.
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToShareNetworkCreateMap()
+	if err != nil {
+		r.Err = err
+		return
+	}
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
+		OkCodes: []int{200, 202},
+	})
+	return
+}
diff --git a/openstack/sharedfilesystems/v2/sharenetworks/results.go b/openstack/sharedfilesystems/v2/sharenetworks/results.go
new file mode 100644
index 0000000..9a2891a
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/sharenetworks/results.go
@@ -0,0 +1,52 @@
+package sharenetworks
+
+import "github.com/gophercloud/gophercloud"
+
+// ShareNetwork contains all the information associated with an OpenStack
+// ShareNetwork.
+type ShareNetwork struct {
+	// The Share Network ID
+	ID string `json:"id"`
+	// The UUID of the project where the share network was created
+	ProjectID string `json:"project_id"`
+	// The neutron network ID
+	NeutronNetID string `json:"neutron_net_id"`
+	// The neutron subnet ID
+	NeutronSubnetID string `json:"neutron_subnet_id"`
+	// The nova network ID
+	NovaNetID string `json:"nova_net_id"`
+	// The network type. A valid value is VLAN, VXLAN, GRE or flat
+	NetworkType string `json:"network_type"`
+	// The segmentation ID
+	SegmentationID int `json:"segmentation_id"`
+	// The IP block from which to allocate the network, in CIDR notation
+	CIDR string `json:"cidr"`
+	// The IP version of the network. A valid value is 4 or 6
+	IPVersion int `json:"ip_version"`
+	// The Share Network name
+	Name string `json:"name"`
+	// The Share Network description
+	Description string `json:"description"`
+	// The date and time stamp when the Share Network was created
+	CreatedAt string `json:"created_at"`
+	// The date and time stamp when the Share Network was updated
+	UpdatedAt string `json:"updated_at"`
+}
+
+type commonResult struct {
+	gophercloud.Result
+}
+
+// Extract will get the ShareNetwork object out of the commonResult object.
+func (r commonResult) Extract() (*ShareNetwork, error) {
+	var s struct {
+		ShareNetwork *ShareNetwork `json:"share_network"`
+	}
+	err := r.ExtractInto(&s)
+	return s.ShareNetwork, err
+}
+
+// CreateResult contains the response body and error from a Create request.
+type CreateResult struct {
+	commonResult
+}
diff --git a/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures.go b/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures.go
new file mode 100644
index 0000000..bc61084
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures.go
@@ -0,0 +1,63 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func createReq(name, description, network, subnetwork string) string {
+	return fmt.Sprintf(`{
+		"share_network": {
+			"name": "%s",
+			"description": "%s",
+			"neutron_net_id": "%s",
+			"neutron_subnet_id": "%s"
+		}
+	}`, name, description, network, subnetwork)
+}
+
+func createResp(name, description, network, subnetwork string) string {
+	return fmt.Sprintf(`
+	{
+		"share_network": {
+			"name": "%s",
+			"description": "%s",
+			"segmentation_id": null,
+			"created_at": "2015-09-07T14:37:00.583656",
+			"updated_at": null,
+			"id": "77eb3421-4549-4789-ac39-0d5185d68c29",
+			"neutron_net_id": "%s",
+			"neutron_subnet_id": "%s",
+			"ip_version": null,
+			"nova_net_id": null,
+			"cidr": null,
+			"project_id": "e10a683c20da41248cfd5e1ab3d88c62",
+			"network_type": null
+		}
+	}`, name, description, network, subnetwork)
+}
+
+func MockCreateResponse(t *testing.T) {
+	th.Mux.HandleFunc("/share-networks", 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, createReq("my_network",
+			"This is my share network",
+			"998b42ee-2cee-4d36-8b95-67b5ca1f2109",
+			"53482b62-2c84-4a53-b6ab-30d9d9800d06"))
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusAccepted)
+
+		fmt.Fprintf(w, createResp("my_network",
+			"This is my share network",
+			"998b42ee-2cee-4d36-8b95-67b5ca1f2109",
+			"53482b62-2c84-4a53-b6ab-30d9d9800d06"))
+	})
+}
diff --git a/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go
new file mode 100644
index 0000000..9a00885
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go
@@ -0,0 +1,32 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharenetworks"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// Verifies that a share network can be created correctly
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockCreateResponse(t)
+
+	options := &sharenetworks.CreateOpts{
+		Name:            "my_network",
+		Description:     "This is my share network",
+		NeutronNetID:    "998b42ee-2cee-4d36-8b95-67b5ca1f2109",
+		NeutronSubnetID: "53482b62-2c84-4a53-b6ab-30d9d9800d06",
+	}
+
+	n, err := sharenetworks.Create(client.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, n.Name, "my_network")
+	th.AssertEquals(t, n.Description, "This is my share network")
+	th.AssertEquals(t, n.NeutronNetID, "998b42ee-2cee-4d36-8b95-67b5ca1f2109")
+	th.AssertEquals(t, n.NeutronSubnetID, "53482b62-2c84-4a53-b6ab-30d9d9800d06")
+}
diff --git a/openstack/sharedfilesystems/v2/sharenetworks/urls.go b/openstack/sharedfilesystems/v2/sharenetworks/urls.go
new file mode 100644
index 0000000..637ea98
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/sharenetworks/urls.go
@@ -0,0 +1,7 @@
+package sharenetworks
+
+import "github.com/gophercloud/gophercloud"
+
+func createURL(c *gophercloud.ServiceClient) string {
+	return c.ServiceURL("share-networks")
+}
diff --git a/script/acceptancetest_environments/keystonev2-lbaasv1.sh b/script/acceptancetest_environments/keystonev2-lbaasv1.sh
index cd520ac..103c6fc 100644
--- a/script/acceptancetest_environments/keystonev2-lbaasv1.sh
+++ b/script/acceptancetest_environments/keystonev2-lbaasv1.sh
@@ -19,6 +19,7 @@
 # * Neutron v2
 # * Neutron LBaaS v1.0
 # * Neutron FWaaS v2.0
+# * Manila v2
 #
 # Go 1.6 is also installed.
 
@@ -137,6 +138,9 @@
 #enable_plugin zaqar https://github.com/openstack/zaqar
 #enable_service zaqar-server
 
+# Enable Manila
+enable_plugin manila https://github.com/openstack/manila
+
 # Automatically download and register a VM image that Heat can launch
 # For more information on Heat and DevStack see
 # http://docs.openstack.org/developer/heat/getting_started/on_devstack.html