Feature/filestorage sharetype create (#141)

* sfs: Add support for share type Create

* sfs: Add acceptance tests for share type Create

* Fix comment

* sfs: Fix comments about share type create
diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetypes.go b/acceptance/openstack/sharedfilesystems/v2/sharetypes.go
new file mode 100644
index 0000000..7f393d6
--- /dev/null
+++ b/acceptance/openstack/sharedfilesystems/v2/sharetypes.go
@@ -0,0 +1,45 @@
+package v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetypes"
+)
+
+// CreateShareType will create a share type with a random name. An
+// error will be returned if the share type was unable to be created.
+func CreateShareType(t *testing.T, client *gophercloud.ServiceClient) (*sharetypes.ShareType, error) {
+	if testing.Short() {
+		t.Skip("Skipping test that requires share type creation in short mode.")
+	}
+
+	shareTypeName := tools.RandomString("ACPTTEST", 16)
+	t.Logf("Attempting to create share type: %s", shareTypeName)
+
+	extraSpecsOps := sharetypes.ExtraSpecsOpts{
+		DriverHandlesShareServers: true,
+	}
+
+	createOpts := sharetypes.CreateOpts{
+		Name:       shareTypeName,
+		IsPublic:   true,
+		ExtraSpecs: extraSpecsOps,
+	}
+
+	shareType, err := sharetypes.Create(client, createOpts).Extract()
+	if err != nil {
+		return shareType, err
+	}
+
+	return shareType, nil
+}
+
+// PrintShareType will print a share type and all of its attributes.
+func PrintShareType(t *testing.T, shareType *sharetypes.ShareType) {
+	t.Logf("Name: %s", shareType.Name)
+	t.Logf("ID: %s", shareType.ID)
+	t.Logf("OS share type access is public: %t", shareType.IsPublic)
+	t.Logf("Extra specs: %#v", shareType.ExtraSpecs)
+}
diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go
new file mode 100644
index 0000000..6cb9202
--- /dev/null
+++ b/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go
@@ -0,0 +1,23 @@
+package v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+)
+
+func TestShareTypeCreateDestroy(t *testing.T) {
+	client, err := clients.NewSharedFileSystemV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create shared file system client: %v", err)
+	}
+
+	shareType, err := CreateShareType(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create share type: %v", err)
+	}
+
+	//TODO: delete share type when the delete is implemented
+
+	PrintShareType(t, shareType)
+}
diff --git a/openstack/sharedfilesystems/v2/sharetypes/requests.go b/openstack/sharedfilesystems/v2/sharetypes/requests.go
new file mode 100644
index 0000000..97905e3
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/sharetypes/requests.go
@@ -0,0 +1,50 @@
+package sharetypes
+
+import "github.com/gophercloud/gophercloud"
+
+// CreateOptsBuilder allows extensions to add additional parameters to the
+// Create request.
+type CreateOptsBuilder interface {
+	ToShareTypeCreateMap() (map[string]interface{}, error)
+}
+
+// CreateOpts contains options for creating a ShareType. This object is
+// passed to the sharetypes.Create function. For more information about
+// these parameters, see the ShareType object.
+type CreateOpts struct {
+	// The share type name
+	Name string `json:"name" required:"true"`
+	// Indicates whether a share type is publicly accessible
+	IsPublic bool `json:"os-share-type-access:is_public"`
+	// The extra specifications for the share type
+	ExtraSpecs ExtraSpecsOpts `json:"extra_specs" required:"true"`
+}
+
+// ExtraSpecsOpts represent the extra specifications that can be selected for a share type
+type ExtraSpecsOpts struct {
+	// An extra specification that defines the driver mode for share server, or storage, life cycle management
+	DriverHandlesShareServers bool `json:"driver_handles_share_servers" required:"true"`
+	// An extra specification that filters back ends by whether they do or do not support share snapshots
+	SnapshotSupport *bool `json:"snapshot_support,omitempty"`
+}
+
+// ToShareTypeCreateMap assembles a request body based on the contents of a
+// CreateOpts.
+func (opts CreateOpts) ToShareTypeCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "share_type")
+}
+
+// Create will create a new ShareType based on the values in CreateOpts. To
+// extract the ShareType object from the response, call the Extract method
+// on the CreateResult.
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+	b, err := opts.ToShareTypeCreateMap()
+	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/sharetypes/results.go b/openstack/sharedfilesystems/v2/sharetypes/results.go
new file mode 100644
index 0000000..7762dc6
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/sharetypes/results.go
@@ -0,0 +1,36 @@
+package sharetypes
+
+import "github.com/gophercloud/gophercloud"
+
+// ShareType contains all the information associated with an OpenStack
+// ShareType.
+type ShareType struct {
+	// The Share Type ID
+	ID string `json:"id"`
+	// The Share Type name
+	Name string `json:"name"`
+	// Indicates whether a share type is publicly accessible
+	IsPublic bool `json:"os-share-type-access:is_public"`
+	// The required extra specifications for the share type
+	RequiredExtraSpecs map[string]interface{} `json:"required_extra_specs"`
+	// The extra specifications for the share type
+	ExtraSpecs map[string]interface{} `json:"extra_specs"`
+}
+
+type commonResult struct {
+	gophercloud.Result
+}
+
+// Extract will get the ShareType object out of the commonResult object.
+func (r commonResult) Extract() (*ShareType, error) {
+	var s struct {
+		ShareType *ShareType `json:"share_type"`
+	}
+	err := r.ExtractInto(&s)
+	return s.ShareType, err
+}
+
+// CreateResult contains the response body and error from a Create request.
+type CreateResult struct {
+	commonResult
+}
diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures.go b/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures.go
new file mode 100644
index 0000000..f597b76
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures.go
@@ -0,0 +1,61 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	th "github.com/gophercloud/gophercloud/testhelper"
+	fake "github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+func MockCreateResponse(t *testing.T) {
+	th.Mux.HandleFunc("/types", 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, `
+        {
+            "share_type": {
+                "os-share-type-access:is_public": true,
+                "extra_specs": {
+                    "driver_handles_share_servers": true,
+                    "snapshot_support": true
+                },
+                "name": "my_new_share_type"
+            }
+        }`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusAccepted)
+
+		fmt.Fprintf(w, `
+        {
+            "volume_type": {
+                "os-share-type-access:is_public": true,
+                "required_extra_specs": {
+                    "driver_handles_share_servers": true
+                },
+                "extra_specs": {
+                    "snapshot_support": "True",
+                    "driver_handles_share_servers": "True"
+                },
+                "name": "my_new_share_type",
+                "id": "1d600d02-26a7-4b23-af3d-7d51860fe858"
+            },
+            "share_type": {
+                "os-share-type-access:is_public": true,
+                "required_extra_specs": {
+                    "driver_handles_share_servers": true
+                },
+                "extra_specs": {
+                    "snapshot_support": "True",
+                    "driver_handles_share_servers": "True"
+                },
+                "name": "my_new_share_type",
+                "id": "1d600d02-26a7-4b23-af3d-7d51860fe858"
+            }
+        }`)
+	})
+}
diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go
new file mode 100644
index 0000000..bd70607
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go
@@ -0,0 +1,61 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetypes"
+	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/testhelper/client"
+)
+
+// Verifies that a share type can be created correctly
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockCreateResponse(t)
+
+	snapshotSupport := true
+	extraSpecs := sharetypes.ExtraSpecsOpts{
+		DriverHandlesShareServers: true,
+		SnapshotSupport:           &snapshotSupport,
+	}
+
+	options := &sharetypes.CreateOpts{
+		Name:       "my_new_share_type",
+		IsPublic:   true,
+		ExtraSpecs: extraSpecs,
+	}
+
+	st, err := sharetypes.Create(client.ServiceClient(), options).Extract()
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, st.Name, "my_new_share_type")
+	th.AssertEquals(t, st.IsPublic, true)
+}
+
+// Verifies that a share type can't be created if the required parameters are missing
+func TestCreateFails(t *testing.T) {
+	options := &sharetypes.CreateOpts{
+		Name: "my_new_share_type",
+	}
+
+	_, err := sharetypes.Create(client.ServiceClient(), options).Extract()
+	if _, ok := err.(gophercloud.ErrMissingInput); !ok {
+		t.Fatal("ErrMissingInput was expected to occur")
+	}
+
+	extraSpecs := sharetypes.ExtraSpecsOpts{
+		DriverHandlesShareServers: true,
+	}
+
+	options = &sharetypes.CreateOpts{
+		ExtraSpecs: extraSpecs,
+	}
+
+	_, err = sharetypes.Create(client.ServiceClient(), options).Extract()
+	if _, ok := err.(gophercloud.ErrMissingInput); !ok {
+		t.Fatal("ErrMissingInput was expected to occur")
+	}
+}
diff --git a/openstack/sharedfilesystems/v2/sharetypes/urls.go b/openstack/sharedfilesystems/v2/sharetypes/urls.go
new file mode 100644
index 0000000..4ab4f8d
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/sharetypes/urls.go
@@ -0,0 +1,7 @@
+package sharetypes
+
+import "github.com/gophercloud/gophercloud"
+
+func createURL(c *gophercloud.ServiceClient) string {
+	return c.ServiceURL("types")
+}