Skip to content
This repository was archived by the owner on Aug 1, 2023. It is now read-only.

Commit da54614

Browse files
committed
Copy blockstorage v1 to v2
1 parent c54bbac commit da54614

File tree

10 files changed

+709
-0
lines changed

10 files changed

+709
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Package volumes provides information and interaction with volumes in the
2+
// OpenStack Block Storage service. A volume is a detachable block storage
3+
// device, akin to a USB hard drive. It can only be attached to one instance at
4+
// a time.
5+
package volumes
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
package volumes
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/rackspace/gophercloud"
7+
"github.com/rackspace/gophercloud/pagination"
8+
)
9+
10+
// CreateOptsBuilder allows extensions to add additional parameters to the
11+
// Create request.
12+
type CreateOptsBuilder interface {
13+
ToVolumeCreateMap() (map[string]interface{}, error)
14+
}
15+
16+
// CreateOpts contains options for creating a Volume. This object is passed to
17+
// the volumes.Create function. For more information about these parameters,
18+
// see the Volume object.
19+
type CreateOpts struct {
20+
// OPTIONAL
21+
Availability string
22+
// OPTIONAL
23+
Description string
24+
// OPTIONAL
25+
Metadata map[string]string
26+
// OPTIONAL
27+
Name string
28+
// REQUIRED
29+
Size int
30+
// OPTIONAL
31+
SnapshotID, SourceVolID, ImageID string
32+
// OPTIONAL
33+
VolumeType string
34+
}
35+
36+
// ToVolumeCreateMap assembles a request body based on the contents of a
37+
// CreateOpts.
38+
func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) {
39+
v := make(map[string]interface{})
40+
41+
if opts.Size == 0 {
42+
return nil, fmt.Errorf("Required CreateOpts field 'Size' not set.")
43+
}
44+
v["size"] = opts.Size
45+
46+
if opts.Availability != "" {
47+
v["availability_zone"] = opts.Availability
48+
}
49+
if opts.Description != "" {
50+
v["display_description"] = opts.Description
51+
}
52+
if opts.ImageID != "" {
53+
v["imageRef"] = opts.ImageID
54+
}
55+
if opts.Metadata != nil {
56+
v["metadata"] = opts.Metadata
57+
}
58+
if opts.Name != "" {
59+
v["display_name"] = opts.Name
60+
}
61+
if opts.SourceVolID != "" {
62+
v["source_volid"] = opts.SourceVolID
63+
}
64+
if opts.SnapshotID != "" {
65+
v["snapshot_id"] = opts.SnapshotID
66+
}
67+
if opts.VolumeType != "" {
68+
v["volume_type"] = opts.VolumeType
69+
}
70+
71+
return map[string]interface{}{"volume": v}, nil
72+
}
73+
74+
// Create will create a new Volume based on the values in CreateOpts. To extract
75+
// the Volume object from the response, call the Extract method on the
76+
// CreateResult.
77+
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
78+
var res CreateResult
79+
80+
reqBody, err := opts.ToVolumeCreateMap()
81+
if err != nil {
82+
res.Err = err
83+
return res
84+
}
85+
86+
_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
87+
OkCodes: []int{200, 201},
88+
})
89+
return res
90+
}
91+
92+
// Delete will delete the existing Volume with the provided ID.
93+
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
94+
var res DeleteResult
95+
_, res.Err = client.Delete(deleteURL(client, id), nil)
96+
return res
97+
}
98+
99+
// Get retrieves the Volume with the provided ID. To extract the Volume object
100+
// from the response, call the Extract method on the GetResult.
101+
func Get(client *gophercloud.ServiceClient, id string) GetResult {
102+
var res GetResult
103+
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
104+
return res
105+
}
106+
107+
// ListOptsBuilder allows extensions to add additional parameters to the List
108+
// request.
109+
type ListOptsBuilder interface {
110+
ToVolumeListQuery() (string, error)
111+
}
112+
113+
// ListOpts holds options for listing Volumes. It is passed to the volumes.List
114+
// function.
115+
type ListOpts struct {
116+
// admin-only option. Set it to true to see all tenant volumes.
117+
AllTenants bool `q:"all_tenants"`
118+
// List only volumes that contain Metadata.
119+
Metadata map[string]string `q:"metadata"`
120+
// List only volumes that have Name as the display name.
121+
Name string `q:"name"`
122+
// List only volumes that have a status of Status.
123+
Status string `q:"status"`
124+
}
125+
126+
// ToVolumeListQuery formats a ListOpts into a query string.
127+
func (opts ListOpts) ToVolumeListQuery() (string, error) {
128+
q, err := gophercloud.BuildQueryString(opts)
129+
if err != nil {
130+
return "", err
131+
}
132+
return q.String(), nil
133+
}
134+
135+
// List returns Volumes optionally limited by the conditions provided in ListOpts.
136+
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
137+
url := listURL(client)
138+
if opts != nil {
139+
query, err := opts.ToVolumeListQuery()
140+
if err != nil {
141+
return pagination.Pager{Err: err}
142+
}
143+
url += query
144+
}
145+
createPage := func(r pagination.PageResult) pagination.Page {
146+
return ListResult{pagination.SinglePageBase(r)}
147+
}
148+
149+
return pagination.NewPager(client, url, createPage)
150+
}
151+
152+
// UpdateOptsBuilder allows extensions to add additional parameters to the
153+
// Update request.
154+
type UpdateOptsBuilder interface {
155+
ToVolumeUpdateMap() (map[string]interface{}, error)
156+
}
157+
158+
// UpdateOpts contain options for updating an existing Volume. This object is passed
159+
// to the volumes.Update function. For more information about the parameters, see
160+
// the Volume object.
161+
type UpdateOpts struct {
162+
// OPTIONAL
163+
Name string
164+
// OPTIONAL
165+
Description string
166+
// OPTIONAL
167+
Metadata map[string]string
168+
}
169+
170+
// ToVolumeUpdateMap assembles a request body based on the contents of an
171+
// UpdateOpts.
172+
func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) {
173+
v := make(map[string]interface{})
174+
175+
if opts.Description != "" {
176+
v["display_description"] = opts.Description
177+
}
178+
if opts.Metadata != nil {
179+
v["metadata"] = opts.Metadata
180+
}
181+
if opts.Name != "" {
182+
v["display_name"] = opts.Name
183+
}
184+
185+
return map[string]interface{}{"volume": v}, nil
186+
}
187+
188+
// Update will update the Volume with provided information. To extract the updated
189+
// Volume from the response, call the Extract method on the UpdateResult.
190+
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
191+
var res UpdateResult
192+
193+
reqBody, err := opts.ToVolumeUpdateMap()
194+
if err != nil {
195+
res.Err = err
196+
return res
197+
}
198+
199+
_, res.Err = client.Put(updateURL(client, id), reqBody, &res.Body, &gophercloud.RequestOpts{
200+
OkCodes: []int{200},
201+
})
202+
return res
203+
}
204+
205+
// IDFromName is a convienience function that returns a server's ID given its name.
206+
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
207+
volumeCount := 0
208+
volumeID := ""
209+
if name == "" {
210+
return "", fmt.Errorf("A volume name must be provided.")
211+
}
212+
pager := List(client, nil)
213+
pager.EachPage(func(page pagination.Page) (bool, error) {
214+
volumeList, err := ExtractVolumes(page)
215+
if err != nil {
216+
return false, err
217+
}
218+
219+
for _, s := range volumeList {
220+
if s.Name == name {
221+
volumeCount++
222+
volumeID = s.ID
223+
}
224+
}
225+
return true, nil
226+
})
227+
228+
switch volumeCount {
229+
case 0:
230+
return "", fmt.Errorf("Unable to find volume: %s", name)
231+
case 1:
232+
return volumeID, nil
233+
default:
234+
return "", fmt.Errorf("Found %d volumes matching %s", volumeCount, name)
235+
}
236+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package volumes
2+
3+
import (
4+
"testing"
5+
6+
fixtures "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/testing"
7+
"github.com/rackspace/gophercloud/pagination"
8+
th "github.com/rackspace/gophercloud/testhelper"
9+
"github.com/rackspace/gophercloud/testhelper/client"
10+
)
11+
12+
func TestList(t *testing.T) {
13+
th.SetupHTTP()
14+
defer th.TeardownHTTP()
15+
16+
fixtures.MockListResponse(t)
17+
18+
count := 0
19+
20+
List(client.ServiceClient(), &ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
21+
count++
22+
actual, err := ExtractVolumes(page)
23+
if err != nil {
24+
t.Errorf("Failed to extract volumes: %v", err)
25+
return false, err
26+
}
27+
28+
expected := []Volume{
29+
Volume{
30+
ID: "289da7f8-6440-407c-9fb4-7db01ec49164",
31+
Name: "vol-001",
32+
},
33+
Volume{
34+
ID: "96c3bda7-c82a-4f50-be73-ca7621794835",
35+
Name: "vol-002",
36+
},
37+
}
38+
39+
th.CheckDeepEquals(t, expected, actual)
40+
41+
return true, nil
42+
})
43+
44+
if count != 1 {
45+
t.Errorf("Expected 1 page, got %d", count)
46+
}
47+
}
48+
49+
func TestListAll(t *testing.T) {
50+
th.SetupHTTP()
51+
defer th.TeardownHTTP()
52+
53+
fixtures.MockListResponse(t)
54+
55+
allPages, err := List(client.ServiceClient(), &ListOpts{}).AllPages()
56+
th.AssertNoErr(t, err)
57+
actual, err := ExtractVolumes(allPages)
58+
th.AssertNoErr(t, err)
59+
60+
expected := []Volume{
61+
Volume{
62+
ID: "289da7f8-6440-407c-9fb4-7db01ec49164",
63+
Name: "vol-001",
64+
},
65+
Volume{
66+
ID: "96c3bda7-c82a-4f50-be73-ca7621794835",
67+
Name: "vol-002",
68+
},
69+
}
70+
71+
th.CheckDeepEquals(t, expected, actual)
72+
73+
}
74+
75+
func TestGet(t *testing.T) {
76+
th.SetupHTTP()
77+
defer th.TeardownHTTP()
78+
79+
fixtures.MockGetResponse(t)
80+
81+
v, err := Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
82+
th.AssertNoErr(t, err)
83+
84+
th.AssertEquals(t, v.Name, "vol-001")
85+
th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
86+
th.AssertEquals(t, v.Attachments[0]["device"], "/dev/vde")
87+
}
88+
89+
func TestCreate(t *testing.T) {
90+
th.SetupHTTP()
91+
defer th.TeardownHTTP()
92+
93+
fixtures.MockCreateResponse(t)
94+
95+
options := &CreateOpts{Size: 75}
96+
n, err := Create(client.ServiceClient(), options).Extract()
97+
th.AssertNoErr(t, err)
98+
99+
th.AssertEquals(t, n.Size, 4)
100+
th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
101+
}
102+
103+
func TestDelete(t *testing.T) {
104+
th.SetupHTTP()
105+
defer th.TeardownHTTP()
106+
107+
fixtures.MockDeleteResponse(t)
108+
109+
res := Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
110+
th.AssertNoErr(t, res.Err)
111+
}
112+
113+
func TestUpdate(t *testing.T) {
114+
th.SetupHTTP()
115+
defer th.TeardownHTTP()
116+
117+
fixtures.MockUpdateResponse(t)
118+
119+
options := UpdateOpts{Name: "vol-002"}
120+
v, err := Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract()
121+
th.AssertNoErr(t, err)
122+
th.CheckEquals(t, "vol-002", v.Name)
123+
}

0 commit comments

Comments
 (0)