feature/shared file systems: create share (#119)
* sfs/add url for create
* sfs/results: add share type and result type
* sfs/requests: add CreateOpts and Create method
* sfs/tests: add tests for creating a share
* sfs/shares: fixes according to comments
* sfs/create: fix Metadata field naming in Share
diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go
new file mode 100644
index 0000000..f168178
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/shares/requests.go
@@ -0,0 +1,69 @@
+package shares
+
+import (
+ "github.com/gophercloud/gophercloud"
+)
+
+// CreateOptsBuilder allows extensions to add additional parameters to the
+// Create request.
+type CreateOptsBuilder interface {
+ ToShareCreateMap() (map[string]interface{}, error)
+}
+
+// CreateOpts contains the options for create a Share. This object is
+// passed to shares.Create(). For more information about these parameters,
+// please refer to the Share object, or the shared file systems API v2
+// documentation
+type CreateOpts struct {
+ // Defines the share protocol to use
+ ShareProto string `json:"share_proto" required:"true"`
+ // Size in GB
+ Size int `json:"size" required:"true"`
+ // Defines the share name
+ Name string `json:"name,omitempty"`
+ // Share description
+ Description string `json:"description,omitempty"`
+ // DisplayName is equivalent to Name. The API supports using both
+ // This is an inherited attribute from the block storage API
+ DisplayName string `json:"display_name,omitempty"`
+ // DisplayDescription is equivalent to Description. The API supports using bot
+ // This is an inherited attribute from the block storage API
+ DisplayDescription string `json:"display_description,omitempty"`
+ // ShareType defines the sharetype. If omitted, a default share type is used
+ ShareType string `json:"share_type,omitempty"`
+ // VolumeType is deprecated but supported. Either ShareType or VolumeType can be used
+ VolumeType string `json:"volume_type,omitempty"`
+ // The UUID from which to create a share
+ SnapshotID string `json:"snapshot_id,omitempty"`
+ // Determines whether or not the share is public
+ IsPublic *bool `json:"is_public,omitempty"`
+ // Key value pairs of user defined metadata
+ Metadata map[string]string `json:"metadata,omitempty"`
+ // The UUID of the share network to which the share belongs to
+ ShareNetworkID string `json:"share_network_id,omitempty"`
+ // The UUID of the consistency group to which the share belongs to
+ ConsistencyGroupID string `json:"consistency_group_id,omitempty"`
+ // The availability zone of the share
+ AvailabilityZone string `json:"availability_zone,omitempty"`
+}
+
+// ToShareCreateMap assembles a request body based on the contents of a
+// CreateOpts.
+func (opts CreateOpts) ToShareCreateMap() (map[string]interface{}, error) {
+ return gophercloud.BuildRequestBody(opts, "share")
+}
+
+// Create will create a new Share based on the values in CreateOpts. To extract
+// the Share object from the response, call the Extract method on the
+// CreateResult.
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+ b, err := opts.ToShareCreateMap()
+ if err != nil {
+ r.Err = err
+ return
+ }
+ _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
+ OkCodes: []int{200, 201},
+ })
+ return
+}
diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go
new file mode 100644
index 0000000..f303201
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/shares/results.go
@@ -0,0 +1,82 @@
+package shares
+
+import (
+ "github.com/gophercloud/gophercloud"
+)
+
+// Share contains all information associated with an OpenStack Share
+type Share struct {
+ // The availability zone of the share
+ AvailabilityZone string `json:"availability_zone"`
+ // A description of the share
+ Description string `json:"description,omitempty"`
+ // DisplayDescription is inherited from BlockStorage API.
+ // Both Description and DisplayDescription can be used
+ DisplayDescription string `json:"display_description,omitempty"`
+ // DisplayName is inherited from BlockStorage API
+ // Both DisplayName and Name can be used
+ DisplayName string `json:"display_name,omitempty"`
+ // Indicates whether a share has replicas or not.
+ HasReplicas bool `json:"has_replicas"`
+ // The host name of the share
+ Host string `json:"host"`
+ // The UUID of the share
+ ID string `json:"id"`
+ // Indicates the visibility of the share
+ IsPublic bool `json:"is_public,omitempty"`
+ // Share links for pagination
+ Links []map[string]string `json:"links"`
+ // Key, value -pairs of custom metadata
+ Metadata map[string]string `json:"metadata,omitempty"`
+ // The name of the share
+ Name string `json:"name,omitempty"`
+ // The UUID of the project to which this share belongs to
+ ProjectID string `json:"project_id"`
+ // The share replication type
+ ReplicationType string `json:"replication_type,omitempty"`
+ // The UUID of the share network
+ ShareNetworkID string `json:"share_network_id"`
+ // The shared file system protocol
+ ShareProto string `json:"share_proto"`
+ // The UUID of the share server
+ ShareServerID string `json:"share_server_id"`
+ // The UUID of the share type.
+ ShareType string `json:"share_type"`
+ // The name of the share type.
+ ShareTypeName string `json:"share_type_name"`
+ // Size of the share in GB
+ Size int `json:"size"`
+ // UUID of the snapshot from which to create the share
+ SnapshotID string `json:"snapshot_id"`
+ // The share status
+ Status string `json:"status"`
+ // The task state, used for share migration
+ TaskState string `json:"task_state"`
+ // The type of the volume
+ VolumeType string `json:"volume_type,omitempty"`
+ // The UUID of the consistency group this share belongs to
+ ConsistencyGroupID string `json:"consistency_group_id"`
+ // Used for filtering backends which either support or do not support share snapshots
+ SnapshotSupport bool `json:"snapshot_support"`
+ SourceCgsnapshotMemberID string `json:"source_cgsnapshot_member_id"`
+ // Timestamp when the share was created
+ CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
+}
+
+type commonResult struct {
+ gophercloud.Result
+}
+
+// Extract will get the Share object from the commonResult
+func (r commonResult) Extract() (*Share, error) {
+ var s struct {
+ Share *Share `json:"share"`
+ }
+ err := r.ExtractInto(&s)
+ return s.Share, err
+}
+
+// CreateResult contains the result..
+type CreateResult struct {
+ commonResult
+}
diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go
new file mode 100644
index 0000000..3f90946
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go
@@ -0,0 +1,80 @@
+package testing
+
+import (
+ "fmt"
+ th "github.com/gophercloud/gophercloud/testhelper"
+ fake "github.com/gophercloud/gophercloud/testhelper/client"
+ "net/http"
+ "testing"
+)
+
+const (
+ shareEndpoint = "/shares"
+)
+
+var createRequest = `{
+ "share": {
+ "name": "my_test_share",
+ "size": 1,
+ "share_proto": "NFS"
+ }
+ }`
+
+var createResponse = `{
+ "share": {
+ "name": "my_test_share",
+ "share_proto": "NFS",
+ "size": 1,
+ "status": null,
+ "share_server_id": null,
+ "project_id": "16e1ab15c35a457e9c2b2aa189f544e1",
+ "share_type": "25747776-08e5-494f-ab40-a64b9d20d8f7",
+ "share_type_name": "default",
+ "availability_zone": null,
+ "created_at": "2015-09-18T10:25:24.533287",
+ "export_location": null,
+ "links": [
+ {
+ "href": "http://172.18.198.54:8786/v1/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264",
+ "rel": "self"
+ },
+ {
+ "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264",
+ "rel": "bookmark"
+ }
+ ],
+ "share_network_id": null,
+ "export_locations": [],
+ "host": null,
+ "access_rules_status": "active",
+ "has_replicas": false,
+ "replication_type": null,
+ "task_state": null,
+ "snapshot_support": true,
+ "consistency_group_id": "9397c191-8427-4661-a2e8-b23820dc01d4",
+ "source_cgsnapshot_member_id": null,
+ "volume_type": "default",
+ "snapshot_id": null,
+ "is_public": true,
+ "metadata": {
+ "project": "my_app",
+ "aim": "doc"
+ },
+ "id": "011d21e2-fbc3-4e4a-9993-9ea223f73264",
+ "description": "My custom share London"
+ }
+ }`
+
+// MockCreateResponse creates a mock response
+func MockCreateResponse(t *testing.T) {
+ th.Mux.HandleFunc(shareEndpoint, 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, createRequest)
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ fmt.Fprintf(w, createResponse)
+ })
+}
diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go
new file mode 100644
index 0000000..6d69201
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go
@@ -0,0 +1,23 @@
+package testing
+
+import (
+ "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares"
+ th "github.com/gophercloud/gophercloud/testhelper"
+ "github.com/gophercloud/gophercloud/testhelper/client"
+ "testing"
+)
+
+func TestCreate(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ MockCreateResponse(t)
+
+ options := &shares.CreateOpts{Size: 1, Name: "my_test_share", ShareProto: "NFS"}
+ n, err := shares.Create(client.ServiceClient(), options).Extract()
+
+ th.AssertNoErr(t, err)
+ th.AssertEquals(t, n.Name, "my_test_share")
+ th.AssertEquals(t, n.Size, 1)
+ th.AssertEquals(t, n.ShareProto, "NFS")
+}
diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go
new file mode 100644
index 0000000..4fba746
--- /dev/null
+++ b/openstack/sharedfilesystems/v2/shares/urls.go
@@ -0,0 +1,7 @@
+package shares
+
+import "github.com/gophercloud/gophercloud"
+
+func createURL(c *gophercloud.ServiceClient) string {
+ return c.ServiceURL("shares")
+}