Feature/filestorage sharetype list (#143)

* sfs: Add support for share type List

* sfs: Add acceptance tests for share type List

* sfs: Fix unit test for share type List
diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go
index f38b1ab..4f1787c 100644
--- a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go
+++ b/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go
@@ -4,6 +4,7 @@
 	"testing"
 
 	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetypes"
 )
 
 func TestShareTypeCreateDestroy(t *testing.T) {
@@ -21,3 +22,24 @@
 
 	defer DeleteShareType(t, client, shareType)
 }
+
+func TestShareTypeList(t *testing.T) {
+	client, err := clients.NewSharedFileSystemV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a shared file system client: %v", err)
+	}
+
+	allPages, err := sharetypes.List(client, sharetypes.ListOpts{}).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to retrieve share types: %v", err)
+	}
+
+	allShareTypes, err := sharetypes.ExtractShareTypes(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract share types: %v", err)
+	}
+
+	for _, shareType := range allShareTypes {
+		PrintShareType(t, &shareType)
+	}
+}
diff --git a/openstack/sharedfilesystems/v2/sharetypes/requests.go b/openstack/sharedfilesystems/v2/sharetypes/requests.go
index 04153c7..1e63c5f 100644
--- a/openstack/sharedfilesystems/v2/sharetypes/requests.go
+++ b/openstack/sharedfilesystems/v2/sharetypes/requests.go
@@ -1,6 +1,9 @@
 package sharetypes
 
-import "github.com/gophercloud/gophercloud"
+import (
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
+)
 
 // CreateOptsBuilder allows extensions to add additional parameters to the
 // Create request.
@@ -54,3 +57,38 @@
 	_, r.Err = client.Delete(deleteURL(client, id), nil)
 	return
 }
+
+// ListOptsBuilder allows extensions to add additional parameters to the List
+// request.
+type ListOptsBuilder interface {
+	ToShareTypeListQuery() (string, error)
+}
+
+// ListOpts holds options for listing ShareTypes. It is passed to the
+// sharetypes.List function.
+type ListOpts struct {
+	// Select if public types, private types, or both should be listed
+	IsPublic string `q:"is_public"`
+}
+
+// ToShareTypeListQuery formats a ListOpts into a query string.
+func (opts ListOpts) ToShareTypeListQuery() (string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	return q.String(), err
+}
+
+// List returns ShareTypes optionally limited by the conditions provided in ListOpts.
+func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
+	url := listURL(client)
+	if opts != nil {
+		query, err := opts.ToShareTypeListQuery()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		url += query
+	}
+
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
+		return ShareTypePage{pagination.SinglePageBase(r)}
+	})
+}
diff --git a/openstack/sharedfilesystems/v2/sharetypes/results.go b/openstack/sharedfilesystems/v2/sharetypes/results.go
index 7538581..a48543e 100644
--- a/openstack/sharedfilesystems/v2/sharetypes/results.go
+++ b/openstack/sharedfilesystems/v2/sharetypes/results.go
@@ -1,6 +1,9 @@
 package sharetypes
 
-import "github.com/gophercloud/gophercloud"
+import (
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/pagination"
+)
 
 // ShareType contains all the information associated with an OpenStack
 // ShareType.
@@ -39,3 +42,24 @@
 type DeleteResult struct {
 	gophercloud.ErrResult
 }
+
+// ShareTypePage is a pagination.pager that is returned from a call to the List function.
+type ShareTypePage struct {
+	pagination.SinglePageBase
+}
+
+// IsEmpty returns true if a ListResult contains no ShareTypes.
+func (r ShareTypePage) IsEmpty() (bool, error) {
+	shareTypes, err := ExtractShareTypes(r)
+	return len(shareTypes) == 0, err
+}
+
+// ExtractShareTypes extracts and returns ShareTypes. It is used while
+// iterating over a sharetypes.List call.
+func ExtractShareTypes(r pagination.Page) ([]ShareType, error) {
+	var s struct {
+		ShareTypes []ShareType `json:"share_types"`
+	}
+	err := (r.(ShareTypePage)).ExtractInto(&s)
+	return s.ShareTypes, err
+}
diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures.go b/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures.go
index 0e8ba52..5cf7194 100644
--- a/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures.go
+++ b/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures.go
@@ -67,3 +67,69 @@
 		w.WriteHeader(http.StatusAccepted)
 	})
 }
+
+func MockListResponse(t *testing.T) {
+	th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+        {
+            "volume_types": [
+                {
+                    "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": "default",
+                    "id": "be27425c-f807-4500-a056-d00721db45cf"
+                },
+                {
+                    "os-share-type-access:is_public": true,
+                    "required_extra_specs": {
+                        "driver_handles_share_servers": "false"
+                    },
+                    "extra_specs": {
+                        "snapshot_support": "True",
+                        "driver_handles_share_servers": "false"
+                    },
+                    "name": "d",
+                    "id": "f015bebe-c38b-4c49-8832-00143b10253b"
+                }
+            ],
+            "share_types": [
+                {
+                    "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": "default",
+                    "id": "be27425c-f807-4500-a056-d00721db45cf"
+                },
+                {
+                    "os-share-type-access:is_public": true,
+                    "required_extra_specs": {
+                        "driver_handles_share_servers": "false"
+                    },
+                    "extra_specs": {
+                        "snapshot_support": "True",
+                        "driver_handles_share_servers": "false"
+                    },
+                    "name": "d",
+                    "id": "f015bebe-c38b-4c49-8832-00143b10253b"
+                }
+            ]
+        }`)
+	})
+}
diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go
index 22ed2d1..ce20b0f 100644
--- a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go
+++ b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go
@@ -69,3 +69,34 @@
 	res := sharetypes.Delete(client.ServiceClient(), "shareTypeID")
 	th.AssertNoErr(t, res.Err)
 }
+
+// Verifies that share types can be listed correctly
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	MockListResponse(t)
+
+	allPages, err := sharetypes.List(client.ServiceClient(), &sharetypes.ListOpts{}).AllPages()
+	th.AssertNoErr(t, err)
+	actual, err := sharetypes.ExtractShareTypes(allPages)
+	th.AssertNoErr(t, err)
+	expected := []sharetypes.ShareType{
+		{
+			ID:                 "be27425c-f807-4500-a056-d00721db45cf",
+			Name:               "default",
+			IsPublic:           true,
+			ExtraSpecs:         map[string]interface{}{"snapshot_support": "True", "driver_handles_share_servers": "True"},
+			RequiredExtraSpecs: map[string]interface{}{"driver_handles_share_servers": "True"},
+		},
+		{
+			ID:                 "f015bebe-c38b-4c49-8832-00143b10253b",
+			Name:               "d",
+			IsPublic:           true,
+			ExtraSpecs:         map[string]interface{}{"driver_handles_share_servers": "false", "snapshot_support": "True"},
+			RequiredExtraSpecs: map[string]interface{}{"driver_handles_share_servers": "True"},
+		},
+	}
+
+	th.CheckDeepEquals(t, expected, actual)
+}
diff --git a/openstack/sharedfilesystems/v2/sharetypes/urls.go b/openstack/sharedfilesystems/v2/sharetypes/urls.go
index 3fb7f18..5004f97 100644
--- a/openstack/sharedfilesystems/v2/sharetypes/urls.go
+++ b/openstack/sharedfilesystems/v2/sharetypes/urls.go
@@ -9,3 +9,7 @@
 func deleteURL(c *gophercloud.ServiceClient, id string) string {
 	return c.ServiceURL("types", id)
 }
+
+func listURL(c *gophercloud.ServiceClient) string {
+	return createURL(c)
+}