From 57353b083c1c7c31d1984239e901508d69fed71e Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 13:11:26 +0100 Subject: [PATCH 01/51] Copy --- openstack/evs/apiversions/doc.go | 30 ++ openstack/evs/apiversions/errors.go | 23 + openstack/evs/apiversions/requests.go | 13 + openstack/evs/apiversions/results.go | 64 +++ openstack/evs/apiversions/testing/doc.go | 2 + openstack/evs/apiversions/testing/fixtures.go | 141 ++++++ .../evs/apiversions/testing/requests_test.go | 119 +++++ openstack/evs/apiversions/urls.go | 14 + .../evs/extensions/availabilityzones/doc.go | 21 + .../extensions/availabilityzones/requests.go | 13 + .../extensions/availabilityzones/results.go | 33 ++ .../availabilityzones/testing/doc.go | 2 + .../availabilityzones/testing/fixtures.go | 52 +++ .../testing/requests_test.go | 25 ++ .../evs/extensions/availabilityzones/urls.go | 7 + openstack/evs/extensions/backups/doc.go | 124 ++++++ openstack/evs/extensions/backups/requests.go | 303 +++++++++++++ openstack/evs/extensions/backups/results.go | 337 ++++++++++++++ .../extensions/backups/testing/fixtures.go | 300 +++++++++++++ .../backups/testing/requests_test.go | 189 ++++++++ openstack/evs/extensions/backups/urls.go | 39 ++ openstack/evs/extensions/limits/doc.go | 13 + openstack/evs/extensions/limits/requests.go | 13 + openstack/evs/extensions/limits/results.go | 80 ++++ .../evs/extensions/limits/testing/fixtures.go | 129 ++++++ .../limits/testing/requests_test.go | 19 + openstack/evs/extensions/limits/urls.go | 11 + openstack/evs/extensions/quotasets/delete.go | 10 - openstack/evs/extensions/quotasets/doc.go | 60 +++ openstack/evs/extensions/quotasets/get.go | 50 --- .../evs/extensions/quotasets/get_usage.go | 65 --- .../evs/extensions/quotasets/requests.go | 116 +++++ openstack/evs/extensions/quotasets/results.go | 205 +++++++++ .../evs/extensions/quotasets/testing/doc.go | 2 + .../extensions/quotasets/testing/fixtures.go | 172 +++---- .../quotasets/testing/requests_test.go | 37 +- openstack/evs/extensions/quotasets/update.go | 47 -- openstack/evs/extensions/quotasets/urls.go | 21 + .../evs/extensions/schedulerhints/doc.go | 52 +++ .../evs/extensions/schedulerhints/requests.go | 124 ++++++ .../extensions/schedulerhints/testing/doc.go | 2 + .../schedulerhints/testing/requests_test.go | 58 +++ .../evs/extensions/schedulerstats/doc.go | 23 + .../evs/extensions/schedulerstats/requests.go | 43 ++ .../schedulerstats/{list.go => results.go} | 92 ++-- .../schedulerstats/testing/fixtures.go | 41 +- .../schedulerstats/testing/requests_test.go | 31 +- .../evs/extensions/schedulerstats/urls.go | 7 + openstack/evs/extensions/services/doc.go | 22 + openstack/evs/extensions/services/requests.go | 42 ++ .../services/{list.go => results.go} | 69 +-- .../extensions/services/testing/fixtures.go | 9 +- .../services/testing/requests_test.go | 35 +- openstack/evs/extensions/services/urls.go | 7 + .../evs/extensions/volumeactions/attach.go | 31 -- .../volumeactions/begin_detaching.go | 9 - .../evs/extensions/volumeactions/detach.go | 18 - openstack/evs/extensions/volumeactions/doc.go | 108 +++++ .../extensions/volumeactions/extend_size.go | 18 - .../extensions/volumeactions/force_delete.go | 10 - .../volumeactions/initialize_connection.go | 38 -- .../evs/extensions/volumeactions/requests.go | 420 ++++++++++++++++++ .../evs/extensions/volumeactions/reserve.go | 11 - .../evs/extensions/volumeactions/results.go | 221 +++++++++ .../volumeactions/terminate_connection.go | 30 -- .../extensions/volumeactions/testing/doc.go | 2 + .../volumeactions/testing/fixtures.go | 105 ++++- .../volumeactions/testing/requests_test.go | 105 ++++- .../evs/extensions/volumeactions/unreserve.go | 11 - .../extensions/volumeactions/upload_image.go | 123 ----- .../evs/extensions/volumeactions/urls.go | 7 + openstack/evs/extensions/volumehost/doc.go | 26 ++ .../evs/extensions/volumehost/results.go | 7 + openstack/evs/extensions/volumetenants/doc.go | 26 ++ .../results.go} | 3 +- .../evs/extensions/volumetransfers/doc.go | 65 +++ .../extensions/volumetransfers/requests.go | 124 ++++++ .../evs/extensions/volumetransfers/results.go | 102 +++++ .../volumetransfers/testing/fixtures.go | 216 +++++++++ .../volumetransfers/testing/requests_test.go | 90 ++++ .../evs/extensions/volumetransfers/urls.go | 23 + .../evs/noauth/new_block_storage_no_auth.go | 48 -- openstack/evs/noauth/testing/fixtures.go | 19 - openstack/evs/noauth/testing/requests_test.go | 42 -- openstack/evs/v3/attachments/doc.go | 86 ++++ openstack/evs/v3/attachments/requests.go | 180 ++++++++ openstack/evs/v3/attachments/results.go | 120 +++++ .../evs/v3/attachments/testing/fixtures.go | 233 ++++++++++ .../v3/attachments/testing/requests_test.go | 119 +++++ openstack/evs/v3/attachments/urls.go | 27 ++ openstack/evs/v3/attachments/util.go | 22 + openstack/evs/v3/qos/doc.go | 146 ++++++ openstack/evs/v3/qos/requests.go | 328 ++++++++++++++ openstack/evs/v3/qos/results.go | 144 ++++++ openstack/evs/v3/qos/testing/doc.go | 2 + openstack/evs/v3/qos/testing/fixtures.go | 232 ++++++++++ openstack/evs/v3/qos/testing/requests_test.go | 178 ++++++++ openstack/evs/v3/qos/urls.go | 43 ++ openstack/evs/v3/snapshots/doc.go | 61 +++ openstack/evs/v3/snapshots/requests.go | 191 ++++++++ openstack/evs/v3/snapshots/results.go | 139 ++++++ openstack/evs/v3/snapshots/testing/doc.go | 2 + .../evs/v3/snapshots/testing/fixtures.go | 178 ++++++++ .../evs/v3/snapshots/testing/requests_test.go | 131 ++++++ openstack/evs/v3/snapshots/urls.go | 31 ++ openstack/evs/v3/snapshots/util.go | 22 + openstack/evs/v3/volumes/doc.go | 23 + openstack/evs/v3/volumes/requests.go | 197 ++++++-- openstack/evs/v3/volumes/results.go | 80 +++- openstack/evs/v3/volumes/results_job.go | 94 ---- openstack/evs/v3/volumes/testing/doc.go | 2 + openstack/evs/v3/volumes/testing/fixtures.go | 265 +++++++++++ .../evs/v3/volumes/testing/requests_test.go | 282 ++++++++++++ openstack/evs/v3/volumes/urls.go | 22 +- openstack/evs/v3/volumes/util.go | 22 + openstack/evs/v3/volumetypes/doc.go | 164 +++++++ openstack/evs/v3/volumetypes/requests.go | 306 +++++++++++++ openstack/evs/v3/volumetypes/results.go | 194 ++++++++ openstack/evs/v3/volumetypes/testing/doc.go | 2 + .../evs/v3/volumetypes/testing/fixtures.go | 260 +++++++++++ .../v3/volumetypes/testing/requests_test.go | 278 ++++++++++++ openstack/evs/v3/volumetypes/urls.go | 51 +++ 122 files changed, 9514 insertions(+), 959 deletions(-) create mode 100644 openstack/evs/apiversions/doc.go create mode 100644 openstack/evs/apiversions/errors.go create mode 100644 openstack/evs/apiversions/requests.go create mode 100644 openstack/evs/apiversions/results.go create mode 100644 openstack/evs/apiversions/testing/doc.go create mode 100644 openstack/evs/apiversions/testing/fixtures.go create mode 100644 openstack/evs/apiversions/testing/requests_test.go create mode 100644 openstack/evs/apiversions/urls.go create mode 100644 openstack/evs/extensions/availabilityzones/doc.go create mode 100644 openstack/evs/extensions/availabilityzones/requests.go create mode 100644 openstack/evs/extensions/availabilityzones/results.go create mode 100644 openstack/evs/extensions/availabilityzones/testing/doc.go create mode 100644 openstack/evs/extensions/availabilityzones/testing/fixtures.go create mode 100644 openstack/evs/extensions/availabilityzones/testing/requests_test.go create mode 100644 openstack/evs/extensions/availabilityzones/urls.go create mode 100644 openstack/evs/extensions/backups/doc.go create mode 100644 openstack/evs/extensions/backups/requests.go create mode 100644 openstack/evs/extensions/backups/results.go create mode 100644 openstack/evs/extensions/backups/testing/fixtures.go create mode 100644 openstack/evs/extensions/backups/testing/requests_test.go create mode 100644 openstack/evs/extensions/backups/urls.go create mode 100644 openstack/evs/extensions/limits/doc.go create mode 100644 openstack/evs/extensions/limits/requests.go create mode 100644 openstack/evs/extensions/limits/results.go create mode 100644 openstack/evs/extensions/limits/testing/fixtures.go create mode 100644 openstack/evs/extensions/limits/testing/requests_test.go create mode 100644 openstack/evs/extensions/limits/urls.go delete mode 100644 openstack/evs/extensions/quotasets/delete.go create mode 100644 openstack/evs/extensions/quotasets/doc.go delete mode 100644 openstack/evs/extensions/quotasets/get.go delete mode 100644 openstack/evs/extensions/quotasets/get_usage.go create mode 100644 openstack/evs/extensions/quotasets/requests.go create mode 100644 openstack/evs/extensions/quotasets/results.go create mode 100644 openstack/evs/extensions/quotasets/testing/doc.go delete mode 100644 openstack/evs/extensions/quotasets/update.go create mode 100644 openstack/evs/extensions/quotasets/urls.go create mode 100644 openstack/evs/extensions/schedulerhints/doc.go create mode 100644 openstack/evs/extensions/schedulerhints/requests.go create mode 100644 openstack/evs/extensions/schedulerhints/testing/doc.go create mode 100644 openstack/evs/extensions/schedulerhints/testing/requests_test.go create mode 100644 openstack/evs/extensions/schedulerstats/doc.go create mode 100644 openstack/evs/extensions/schedulerstats/requests.go rename openstack/evs/extensions/schedulerstats/{list.go => results.go} (50%) create mode 100644 openstack/evs/extensions/schedulerstats/urls.go create mode 100644 openstack/evs/extensions/services/doc.go create mode 100644 openstack/evs/extensions/services/requests.go rename openstack/evs/extensions/services/{list.go => results.go} (57%) create mode 100644 openstack/evs/extensions/services/urls.go delete mode 100644 openstack/evs/extensions/volumeactions/attach.go delete mode 100644 openstack/evs/extensions/volumeactions/begin_detaching.go delete mode 100644 openstack/evs/extensions/volumeactions/detach.go create mode 100644 openstack/evs/extensions/volumeactions/doc.go delete mode 100644 openstack/evs/extensions/volumeactions/extend_size.go delete mode 100644 openstack/evs/extensions/volumeactions/force_delete.go delete mode 100644 openstack/evs/extensions/volumeactions/initialize_connection.go create mode 100644 openstack/evs/extensions/volumeactions/requests.go delete mode 100644 openstack/evs/extensions/volumeactions/reserve.go create mode 100644 openstack/evs/extensions/volumeactions/results.go delete mode 100644 openstack/evs/extensions/volumeactions/terminate_connection.go create mode 100644 openstack/evs/extensions/volumeactions/testing/doc.go delete mode 100644 openstack/evs/extensions/volumeactions/unreserve.go delete mode 100644 openstack/evs/extensions/volumeactions/upload_image.go create mode 100644 openstack/evs/extensions/volumeactions/urls.go create mode 100644 openstack/evs/extensions/volumehost/doc.go create mode 100644 openstack/evs/extensions/volumehost/results.go create mode 100644 openstack/evs/extensions/volumetenants/doc.go rename openstack/evs/extensions/{volumetenants.go => volumetenants/results.go} (64%) create mode 100644 openstack/evs/extensions/volumetransfers/doc.go create mode 100644 openstack/evs/extensions/volumetransfers/requests.go create mode 100644 openstack/evs/extensions/volumetransfers/results.go create mode 100644 openstack/evs/extensions/volumetransfers/testing/fixtures.go create mode 100644 openstack/evs/extensions/volumetransfers/testing/requests_test.go create mode 100644 openstack/evs/extensions/volumetransfers/urls.go delete mode 100644 openstack/evs/noauth/new_block_storage_no_auth.go delete mode 100644 openstack/evs/noauth/testing/fixtures.go delete mode 100644 openstack/evs/noauth/testing/requests_test.go create mode 100644 openstack/evs/v3/attachments/doc.go create mode 100644 openstack/evs/v3/attachments/requests.go create mode 100644 openstack/evs/v3/attachments/results.go create mode 100644 openstack/evs/v3/attachments/testing/fixtures.go create mode 100644 openstack/evs/v3/attachments/testing/requests_test.go create mode 100644 openstack/evs/v3/attachments/urls.go create mode 100644 openstack/evs/v3/attachments/util.go create mode 100644 openstack/evs/v3/qos/doc.go create mode 100644 openstack/evs/v3/qos/requests.go create mode 100644 openstack/evs/v3/qos/results.go create mode 100644 openstack/evs/v3/qos/testing/doc.go create mode 100644 openstack/evs/v3/qos/testing/fixtures.go create mode 100644 openstack/evs/v3/qos/testing/requests_test.go create mode 100644 openstack/evs/v3/qos/urls.go create mode 100644 openstack/evs/v3/snapshots/doc.go create mode 100644 openstack/evs/v3/snapshots/requests.go create mode 100644 openstack/evs/v3/snapshots/results.go create mode 100644 openstack/evs/v3/snapshots/testing/doc.go create mode 100644 openstack/evs/v3/snapshots/testing/fixtures.go create mode 100644 openstack/evs/v3/snapshots/testing/requests_test.go create mode 100644 openstack/evs/v3/snapshots/urls.go create mode 100644 openstack/evs/v3/snapshots/util.go create mode 100644 openstack/evs/v3/volumes/doc.go delete mode 100644 openstack/evs/v3/volumes/results_job.go create mode 100644 openstack/evs/v3/volumes/testing/doc.go create mode 100644 openstack/evs/v3/volumes/testing/fixtures.go create mode 100644 openstack/evs/v3/volumes/testing/requests_test.go create mode 100644 openstack/evs/v3/volumes/util.go create mode 100644 openstack/evs/v3/volumetypes/doc.go create mode 100644 openstack/evs/v3/volumetypes/requests.go create mode 100644 openstack/evs/v3/volumetypes/results.go create mode 100644 openstack/evs/v3/volumetypes/testing/doc.go create mode 100644 openstack/evs/v3/volumetypes/testing/fixtures.go create mode 100644 openstack/evs/v3/volumetypes/testing/requests_test.go create mode 100644 openstack/evs/v3/volumetypes/urls.go diff --git a/openstack/evs/apiversions/doc.go b/openstack/evs/apiversions/doc.go new file mode 100644 index 000000000..7f0546361 --- /dev/null +++ b/openstack/evs/apiversions/doc.go @@ -0,0 +1,30 @@ +/* +Package apiversions provides information and interaction with the different +API versions for the OpenStack Block Storage service, code-named Cinder. + +Example of Retrieving all API Versions + + allPages, err := apiversions.List(client).AllPages() + if err != nil { + panic("unable to get API versions: " + err.Error()) + } + + allVersions, err := apiversions.ExtractAPIVersions(allPages) + if err != nil { + panic("unable to extract API versions: " + err.Error()) + } + + for _, version := range allVersions { + fmt.Printf("%+v\n", version) + } + +Example of Retrieving an API Version + + version, err := apiversions.Get(client, "v3").Extract() + if err != nil { + panic("unable to get API version: " + err.Error()) + } + + fmt.Printf("%+v\n", version) +*/ +package apiversions diff --git a/openstack/evs/apiversions/errors.go b/openstack/evs/apiversions/errors.go new file mode 100644 index 000000000..8f0f7628d --- /dev/null +++ b/openstack/evs/apiversions/errors.go @@ -0,0 +1,23 @@ +package apiversions + +import ( + "fmt" +) + +// ErrVersionNotFound is the error when the requested API version +// could not be found. +type ErrVersionNotFound struct{} + +func (e ErrVersionNotFound) Error() string { + return fmt.Sprintf("Unable to find requested API version") +} + +// ErrMultipleVersionsFound is the error when a request for an API +// version returns multiple results. +type ErrMultipleVersionsFound struct { + Count int +} + +func (e ErrMultipleVersionsFound) Error() string { + return fmt.Sprintf("Found %d API versions", e.Count) +} diff --git a/openstack/evs/apiversions/requests.go b/openstack/evs/apiversions/requests.go new file mode 100644 index 000000000..d2d3851d1 --- /dev/null +++ b/openstack/evs/apiversions/requests.go @@ -0,0 +1,13 @@ +package apiversions + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List lists all the Cinder API versions available to end-users. +func List(c *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { + return APIVersionPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/evs/apiversions/results.go b/openstack/evs/apiversions/results.go new file mode 100644 index 000000000..08adcb72c --- /dev/null +++ b/openstack/evs/apiversions/results.go @@ -0,0 +1,64 @@ +package apiversions + +import ( + "time" + + "github.com/gophercloud/gophercloud/pagination" +) + +// APIVersion represents an API version for Cinder. +type APIVersion struct { + // ID is the unique identifier of the API version. + ID string `json:"id"` + + // MinVersion is the minimum microversion supported. + MinVersion string `json:"min_version"` + + // Status represents the status of the API version. + Status string `json:"status"` + + // Updated is the date the API version was updated. + Updated time.Time `json:"updated"` + + // Version is the current version and microversion. + Version string `json:"version"` +} + +// APIVersionPage is the page returned by a pager when traversing over a +// collection of API versions. +type APIVersionPage struct { + pagination.SinglePageBase +} + +// IsEmpty checks whether an APIVersionPage struct is empty. +func (r APIVersionPage) IsEmpty() (bool, error) { + is, err := ExtractAPIVersions(r) + return len(is) == 0, err +} + +// ExtractAPIVersions takes a collection page, extracts all of the elements, +// and returns them a slice of APIVersion structs. It is effectively a cast. +func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { + var s struct { + Versions []APIVersion `json:"versions"` + } + err := (r.(APIVersionPage)).ExtractInto(&s) + return s.Versions, err +} + +// ExtractAPIVersion takes a List result and extracts a single requested +// version, which is returned as an APIVersion +func ExtractAPIVersion(r pagination.Page, v string) (*APIVersion, error) { + allVersions, err := ExtractAPIVersions(r) + if err != nil { + return nil, err + } + + for _, version := range allVersions { + if version.ID == v { + return &version, nil + } + } + + return nil, ErrVersionNotFound{} +} diff --git a/openstack/evs/apiversions/testing/doc.go b/openstack/evs/apiversions/testing/doc.go new file mode 100644 index 000000000..12e4bda0f --- /dev/null +++ b/openstack/evs/apiversions/testing/doc.go @@ -0,0 +1,2 @@ +// apiversions_v1 +package testing diff --git a/openstack/evs/apiversions/testing/fixtures.go b/openstack/evs/apiversions/testing/fixtures.go new file mode 100644 index 000000000..3be1ee66b --- /dev/null +++ b/openstack/evs/apiversions/testing/fixtures.go @@ -0,0 +1,141 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const APIListResponse = ` +{ + "versions": [ + { + "id": "v1.0", + "links": [ + { + "href": "http://docs.openstack.org/", + "rel": "describedby", + "type": "text/html" + }, + { + "href": "http://localhost:8776/v1/", + "rel": "self" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.volume+json;version=1" + } + ], + "min_version": "", + "status": "DEPRECATED", + "updated": "2016-05-02T20:25:19Z", + "version": "" + }, + { + "id": "v2.0", + "links": [ + { + "href": "http://docs.openstack.org/", + "rel": "describedby", + "type": "text/html" + }, + { + "href": "http://localhost:8776/v2/", + "rel": "self" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.volume+json;version=1" + } + ], + "min_version": "", + "status": "SUPPORTED", + "updated": "2014-06-28T12:20:21Z", + "version": "" + }, + { + "id": "v3.0", + "links": [ + { + "href": "http://docs.openstack.org/", + "rel": "describedby", + "type": "text/html" + }, + { + "href": "http://localhost:8776/v3/", + "rel": "self" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.volume+json;version=1" + } + ], + "min_version": "3.0", + "status": "CURRENT", + "updated": "2016-02-08T12:20:21Z", + "version": "3.27" + } + ] +} +` + +const APIListOldResponse = ` +{ + "versions": [ + { + "status": "CURRENT", + "updated": "2012-01-04T11:33:21Z", + "id": "v1.0", + "links": [ + { + "href": "http://23.253.228.211:8776/v1/", + "rel": "self" + } + ] + }, + { + "status": "CURRENT", + "updated": "2012-11-21T11:33:21Z", + "id": "v2.0", + "links": [ + { + "href": "http://23.253.228.211:8776/v2/", + "rel": "self" + } + ] + } + ] +}` + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, APIListResponse) + }) +} + +func MockListOldResponse(t *testing.T) { + th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, APIListOldResponse) + }) +} diff --git a/openstack/evs/apiversions/testing/requests_test.go b/openstack/evs/apiversions/testing/requests_test.go new file mode 100644 index 000000000..ca9688243 --- /dev/null +++ b/openstack/evs/apiversions/testing/requests_test.go @@ -0,0 +1,119 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/apiversions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListVersions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + actual, err := apiversions.ExtractAPIVersions(allVersions) + th.AssertNoErr(t, err) + + expected := []apiversions.APIVersion{ + { + ID: "v1.0", + Status: "DEPRECATED", + Updated: time.Date(2016, 5, 2, 20, 25, 19, 0, time.UTC), + }, + { + ID: "v2.0", + Status: "SUPPORTED", + Updated: time.Date(2014, 6, 28, 12, 20, 21, 0, time.UTC), + }, + { + ID: "v3.0", + MinVersion: "3.0", + Status: "CURRENT", + Updated: time.Date(2016, 2, 8, 12, 20, 21, 0, time.UTC), + Version: "3.27", + }, + } + + th.AssertDeepEquals(t, expected, actual) +} + +func TestListOldVersions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListOldResponse(t) + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + actual, err := apiversions.ExtractAPIVersions(allVersions) + th.AssertNoErr(t, err) + + expected := []apiversions.APIVersion{ + { + ID: "v1.0", + Status: "CURRENT", + Updated: time.Date(2012, 1, 4, 11, 33, 21, 0, time.UTC), + }, + { + ID: "v2.0", + Status: "CURRENT", + Updated: time.Date(2012, 11, 21, 11, 33, 21, 0, time.UTC), + }, + } + + th.AssertDeepEquals(t, expected, actual) +} + +func TestGetVersion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + actual, err := apiversions.ExtractAPIVersion(allVersions, "v3.0") + th.AssertNoErr(t, err) + + expected := apiversions.APIVersion{ + ID: "v3.0", + MinVersion: "3.0", + Status: "CURRENT", + Updated: time.Date(2016, 2, 8, 12, 20, 21, 0, time.UTC), + Version: "3.27", + } + + th.AssertEquals(t, actual.ID, expected.ID) + th.AssertEquals(t, actual.Status, expected.Status) + th.AssertEquals(t, actual.Updated, expected.Updated) +} + +func TestGetOldVersion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListOldResponse(t) + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + actual, err := apiversions.ExtractAPIVersion(allVersions, "v2.0") + th.AssertNoErr(t, err) + + expected := apiversions.APIVersion{ + ID: "v2.0", + MinVersion: "", + Status: "CURRENT", + Updated: time.Date(2012, 11, 21, 11, 33, 21, 0, time.UTC), + Version: "", + } + + th.AssertEquals(t, actual.ID, expected.ID) + th.AssertEquals(t, actual.Status, expected.Status) + th.AssertEquals(t, actual.Updated, expected.Updated) +} diff --git a/openstack/evs/apiversions/urls.go b/openstack/evs/apiversions/urls.go new file mode 100644 index 000000000..a6a35d422 --- /dev/null +++ b/openstack/evs/apiversions/urls.go @@ -0,0 +1,14 @@ +package apiversions + +import ( + "strings" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" +) + +func listURL(c *gophercloud.ServiceClient) string { + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + return endpoint +} diff --git a/openstack/evs/extensions/availabilityzones/doc.go b/openstack/evs/extensions/availabilityzones/doc.go new file mode 100644 index 000000000..29faa8dcb --- /dev/null +++ b/openstack/evs/extensions/availabilityzones/doc.go @@ -0,0 +1,21 @@ +/* +Package availabilityzones provides the ability to get lists of +available volume availability zones. + +Example of Get Availability Zone Information + + allPages, err := availabilityzones.List(volumeClient).AllPages() + if err != nil { + panic(err) + } + + availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) + if err != nil { + panic(err) + } + + for _, zoneInfo := range availabilityZoneInfo { + fmt.Printf("%+v\n", zoneInfo) + } +*/ +package availabilityzones diff --git a/openstack/evs/extensions/availabilityzones/requests.go b/openstack/evs/extensions/availabilityzones/requests.go new file mode 100644 index 000000000..df10b856e --- /dev/null +++ b/openstack/evs/extensions/availabilityzones/requests.go @@ -0,0 +1,13 @@ +package availabilityzones + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List will return the existing availability zones. +func List(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { + return AvailabilityZonePage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/evs/extensions/availabilityzones/results.go b/openstack/evs/extensions/availabilityzones/results.go new file mode 100644 index 000000000..0e115411c --- /dev/null +++ b/openstack/evs/extensions/availabilityzones/results.go @@ -0,0 +1,33 @@ +package availabilityzones + +import ( + "github.com/gophercloud/gophercloud/pagination" +) + +// ZoneState represents the current state of the availability zone. +type ZoneState struct { + // Returns true if the availability zone is available + Available bool `json:"available"` +} + +// AvailabilityZone contains all the information associated with an OpenStack +// AvailabilityZone. +type AvailabilityZone struct { + // The availability zone name + ZoneName string `json:"zoneName"` + ZoneState ZoneState `json:"zoneState"` +} + +type AvailabilityZonePage struct { + pagination.SinglePageBase +} + +// ExtractAvailabilityZones returns a slice of AvailabilityZones contained in a +// single page of results. +func ExtractAvailabilityZones(r pagination.Page) ([]AvailabilityZone, error) { + var s struct { + AvailabilityZoneInfo []AvailabilityZone `json:"availabilityZoneInfo"` + } + err := (r.(AvailabilityZonePage)).ExtractInto(&s) + return s.AvailabilityZoneInfo, err +} diff --git a/openstack/evs/extensions/availabilityzones/testing/doc.go b/openstack/evs/extensions/availabilityzones/testing/doc.go new file mode 100644 index 000000000..a4408d7a0 --- /dev/null +++ b/openstack/evs/extensions/availabilityzones/testing/doc.go @@ -0,0 +1,2 @@ +// availabilityzones unittests +package testing diff --git a/openstack/evs/extensions/availabilityzones/testing/fixtures.go b/openstack/evs/extensions/availabilityzones/testing/fixtures.go new file mode 100644 index 000000000..4b500e484 --- /dev/null +++ b/openstack/evs/extensions/availabilityzones/testing/fixtures.go @@ -0,0 +1,52 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + az "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const GetOutput = ` +{ + "availabilityZoneInfo": [ + { + "zoneName": "internal", + "zoneState": { + "available": true + } + }, + { + "zoneName": "nova", + "zoneState": { + "available": true + } + } + ] +}` + +var AZResult = []az.AvailabilityZone{ + { + ZoneName: "internal", + ZoneState: az.ZoneState{Available: true}, + }, + { + ZoneName: "nova", + ZoneState: az.ZoneState{Available: true}, + }, +} + +// HandleGetSuccessfully configures the test server to respond to a Get request +// for availability zone information. +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-availability-zone", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, GetOutput) + }) +} diff --git a/openstack/evs/extensions/availabilityzones/testing/requests_test.go b/openstack/evs/extensions/availabilityzones/testing/requests_test.go new file mode 100644 index 000000000..39f41bf09 --- /dev/null +++ b/openstack/evs/extensions/availabilityzones/testing/requests_test.go @@ -0,0 +1,25 @@ +package testing + +import ( + "testing" + + az "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// Verifies that availability zones can be listed correctly +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGetSuccessfully(t) + + allPages, err := az.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + + actual, err := az.ExtractAvailabilityZones(allPages) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, AZResult, actual) +} diff --git a/openstack/evs/extensions/availabilityzones/urls.go b/openstack/evs/extensions/availabilityzones/urls.go new file mode 100644 index 000000000..fb4cdcf4e --- /dev/null +++ b/openstack/evs/extensions/availabilityzones/urls.go @@ -0,0 +1,7 @@ +package availabilityzones + +import "github.com/gophercloud/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-availability-zone") +} diff --git a/openstack/evs/extensions/backups/doc.go b/openstack/evs/extensions/backups/doc.go new file mode 100644 index 000000000..f96e6d3de --- /dev/null +++ b/openstack/evs/extensions/backups/doc.go @@ -0,0 +1,124 @@ +/* +Package backups provides information and interaction with backups in the +OpenStack Block Storage service. A backup is a point in time copy of the +data contained in an external storage volume, and can be controlled +programmatically. + +Example to List Backups + + listOpts := backups.ListOpts{ + VolumeID: "uuid", + } + + allPages, err := backups.List(client, listOpts).AllPages() + if err != nil { + panic(err) + } + + allBackups, err := backups.ExtractBackups(allPages) + if err != nil { + panic(err) + } + + for _, backup := range allBackups { + fmt.Println(backup) + } + +Example to Create a Backup + + createOpts := backups.CreateOpts{ + VolumeID: "uuid", + Name: "my-backup", + } + + backup, err := backups.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(backup) + +Example to Update a Backup + + updateOpts := backups.UpdateOpts{ + Name: "new-name", + } + + backup, err := backups.Update(client, "uuid", updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(backup) + +Example to Restore a Backup to a Volume + + options := backups.RestoreOpts{ + VolumeID: "1234", + Name: "vol-001", + } + + restore, err := backups.RestoreFromBackup(client, "uuid", options).Extract() + if err != nil { + panic(err) + } + + fmt.Println(restore) + +Example to Delete a Backup + + err := backups.Delete(client, "uuid").ExtractErr() + if err != nil { + panic(err) + } + +Example to Export a Backup + + export, err := backups.Export(client, "uuid").Extract() + if err != nil { + panic(err) + } + + fmt.Println(export) + +Example to Import a Backup + + status := "available" + availabilityZone := "region1b" + host := "cinder-backup-host1" + serviceMetadata := "volume_cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959/20200311192855/az_regionb_backup_b87bb1e5-0d4e-445e-a548-5ae742562bac" + size := 1 + objectCount := 2 + container := "my-test-backup" + service := "cinder.backup.drivers.swift.SwiftBackupDriver" + backupURL, _ := json.Marshal(backups.ImportBackup{ + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + Status: &status, + AvailabilityZone: &availabilityZone, + VolumeID: "cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959", + UpdatedAt: time.Date(2020, 3, 11, 19, 29, 8, 0, time.UTC), + Host: &host, + UserID: "93514e04-a026-4f60-8176-395c859501dd", + ServiceMetadata: &serviceMetadata, + Size: &size, + ObjectCount: &objectCount, + Container: &container, + Service: &service, + CreatedAt: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + DataTimestamp: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + ProjectID: "14f1c1f5d12b4755b94edef78ff8b325", + }) + + options := backups.ImportOpts{ + BackupService: "cinder.backup.drivers.swift.SwiftBackupDriver", + BackupURL: backupURL, + } + + backup, err := backups.Import(client, options).Extract() + if err != nil { + panic(err) + } + + fmt.Println(backup) +*/ +package backups diff --git a/openstack/evs/extensions/backups/requests.go b/openstack/evs/extensions/backups/requests.go new file mode 100644 index 000000000..4eb123b9f --- /dev/null +++ b/openstack/evs/extensions/backups/requests.go @@ -0,0 +1,303 @@ +package backups + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToBackupCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for creating a Backup. This object is passed to +// the backups.Create function. For more information about these parameters, +// see the Backup object. +type CreateOpts struct { + // VolumeID is the ID of the volume to create the backup from. + VolumeID string `json:"volume_id" required:"true"` + + // Force will force the creation of a backup regardless of the + //volume's status. + Force bool `json:"force,omitempty"` + + // Name is the name of the backup. + Name string `json:"name,omitempty"` + + // Description is the description of the backup. + Description string `json:"description,omitempty"` + + // Metadata is metadata for the backup. + // Requires microversion 3.43 or later. + Metadata map[string]string `json:"metadata,omitempty"` + + // Container is a container to store the backup. + Container string `json:"container,omitempty"` + + // Incremental is whether the backup should be incremental or not. + Incremental bool `json:"incremental,omitempty"` + + // SnapshotID is the ID of a snapshot to backup. + SnapshotID string `json:"snapshot_id,omitempty"` + + // AvailabilityZone is an availability zone to locate the volume or snapshot. + // Requires microversion 3.51 or later. + AvailabilityZone string `json:"availability_zone,omitempty"` +} + +// ToBackupCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToBackupCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "backup") +} + +// Create will create a new Backup based on the values in CreateOpts. To +// extract the Backup object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToBackupCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete will delete the existing Backup with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get retrieves the Backup with the provided ID. To extract the Backup +// object from the response, call the Extract method on the GetResult. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToBackupListQuery() (string, error) +} + +type ListOpts struct { + // AllTenants will retrieve backups of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Name will filter by the specified backup name. + // This does not work in later microversions. + Name string `q:"name"` + + // Status will filter by the specified status. + // This does not work in later microversions. + Status string `q:"status"` + + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required to use this. + TenantID string `q:"project_id"` + + // VolumeID will filter by a specified volume ID. + // This does not work in later microversions. + VolumeID string `q:"volume_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToBackupListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToBackupListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Backups 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.ToBackupListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return BackupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// ListDetailOptsBuilder allows extensions to add additional parameters to the ListDetail +// request. +type ListDetailOptsBuilder interface { + ToBackupListDetailQuery() (string, error) +} + +type ListDetailOpts struct { + // AllTenants will retrieve backups of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` + + // True to include `count` in the API response, supported from version 3.45 + WithCount bool `q:"with_count"` +} + +// ToBackupListDetailQuery formats a ListDetailOpts into a query string. +func (opts ListDetailOpts) ToBackupListDetailQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListDetail returns more detailed information about Backups optionally +// limited by the conditions provided in ListDetailOpts. +func ListDetail(client *gophercloud.ServiceClient, opts ListDetailOptsBuilder) pagination.Pager { + url := listDetailURL(client) + if opts != nil { + query, err := opts.ToBackupListDetailQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return BackupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToBackupUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Backup. +type UpdateOpts struct { + // Name is the name of the backup. + Name *string `json:"name,omitempty"` + + // Description is the description of the backup. + Description *string `json:"description,omitempty"` + + // Metadata is metadata for the backup. + // Requires microversion 3.43 or later. + Metadata map[string]string `json:"metadata,omitempty"` +} + +// ToBackupUpdateMap assembles a request body based on the contents of +// an UpdateOpts. +func (opts UpdateOpts) ToBackupUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Update will update the Backup with provided information. To extract +// the updated Backup from the response, call the Extract method on the +// UpdateResult. +// Requires microversion 3.9 or later. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToBackupUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RestoreOpts contains options for restoring a Backup. This object is passed to +// the backups.RestoreFromBackup function. +type RestoreOpts struct { + // VolumeID is the ID of the existing volume to restore the backup to. + VolumeID string `json:"volume_id,omitempty"` + + // Name is the name of the new volume to restore the backup to. + Name string `json:"name,omitempty"` +} + +// ToRestoreMap assembles a request body based on the contents of a +// RestoreOpts. +func (opts RestoreOpts) ToRestoreMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "restore") +} + +// RestoreFromBackup will restore a Backup to a volume based on the values in +// RestoreOpts. To extract the Restore object from the response, call the +// Extract method on the RestoreResult. +func RestoreFromBackup(client *gophercloud.ServiceClient, id string, opts RestoreOpts) (r RestoreResult) { + b, err := opts.ToRestoreMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(restoreURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Export will export a Backup information. To extract the Backup export record +// object from the response, call the Extract method on the ExportResult. +func Export(client *gophercloud.ServiceClient, id string) (r ExportResult) { + resp, err := client.Get(exportURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ImportOpts contains options for importing a Backup. This object is passed to +// the backups.ImportBackup function. +type ImportOpts BackupRecord + +// ToBackupImportMap assembles a request body based on the contents of a +// ImportOpts. +func (opts ImportOpts) ToBackupImportMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "backup-record") +} + +// Import will import a Backup data to a backup based on the values in +// ImportOpts. To extract the Backup object from the response, call the +// Extract method on the ImportResult. +func Import(client *gophercloud.ServiceClient, opts ImportOpts) (r ImportResult) { + b, err := opts.ToBackupImportMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(importURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/extensions/backups/results.go b/openstack/evs/extensions/backups/results.go new file mode 100644 index 000000000..a575498de --- /dev/null +++ b/openstack/evs/extensions/backups/results.go @@ -0,0 +1,337 @@ +package backups + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Backup contains all the information associated with a Cinder Backup. +type Backup struct { + // ID is the Unique identifier of the backup. + ID string `json:"id"` + + // CreatedAt is the date the backup was created. + CreatedAt time.Time `json:"-"` + + // UpdatedAt is the date the backup was updated. + UpdatedAt time.Time `json:"-"` + + // Name is the display name of the backup. + Name string `json:"name"` + + // Description is the description of the backup. + Description string `json:"description"` + + // VolumeID is the ID of the Volume from which this backup was created. + VolumeID string `json:"volume_id"` + + // SnapshotID is the ID of the snapshot from which this backup was created. + SnapshotID string `json:"snapshot_id"` + + // Status is the status of the backup. + Status string `json:"status"` + + // Size is the size of the backup, in GB. + Size int `json:"size"` + + // Object Count is the number of objects in the backup. + ObjectCount int `json:"object_count"` + + // Container is the container where the backup is stored. + Container string `json:"container"` + + // HasDependentBackups is whether there are other backups + // depending on this backup. + HasDependentBackups bool `json:"has_dependent_backups"` + + // FailReason has the reason for the backup failure. + FailReason string `json:"fail_reason"` + + // IsIncremental is whether this is an incremental backup. + IsIncremental bool `json:"is_incremental"` + + // DataTimestamp is the time when the data on the volume was first saved. + DataTimestamp time.Time `json:"-"` + + // ProjectID is the ID of the project that owns the backup. This is + // an admin-only field. + ProjectID string `json:"os-backup-project-attr:project_id"` + + // Metadata is metadata about the backup. + // This requires microversion 3.43 or later. + Metadata *map[string]string `json:"metadata"` + + // AvailabilityZone is the Availability Zone of the backup. + // This requires microversion 3.51 or later. + AvailabilityZone *string `json:"availability_zone"` +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} + +// BackupPage is a pagination.Pager that is returned from a call to the List function. +type BackupPage struct { + pagination.LinkedPageBase +} + +// UnmarshalJSON converts our JSON API response into our backup struct +func (r *Backup) UnmarshalJSON(b []byte) error { + type tmp Backup + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + DataTimestamp gophercloud.JSONRFC3339MilliNoZ `json:"data_timestamp"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Backup(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.DataTimestamp = time.Time(s.DataTimestamp) + + return err +} + +// IsEmpty returns true if a BackupPage contains no Backups. +func (r BackupPage) IsEmpty() (bool, error) { + volumes, err := ExtractBackups(r) + return len(volumes) == 0, err +} + +func (page BackupPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"backups_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractBackups extracts and returns Backups. It is used while iterating over a backups.List call. +func ExtractBackups(r pagination.Page) ([]Backup, error) { + var s []Backup + err := ExtractBackupsInto(r, &s) + return s, err +} + +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Backup object out of the commonResult object. +func (r commonResult) Extract() (*Backup, error) { + var s Backup + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "backup") +} + +func ExtractBackupsInto(r pagination.Page, v interface{}) error { + return r.(BackupPage).Result.ExtractIntoSlicePtr(v, "backups") +} + +// RestoreResult contains the response body and error from a restore request. +type RestoreResult struct { + commonResult +} + +// Restore contains all the information associated with a Cinder Backup restore +// response. +type Restore struct { + // BackupID is the Unique identifier of the backup. + BackupID string `json:"backup_id"` + + // VolumeID is the Unique identifier of the volume. + VolumeID string `json:"volume_id"` + + // Name is the name of the volume, where the backup was restored to. + VolumeName string `json:"volume_name"` +} + +// Extract will get the Backup restore object out of the RestoreResult object. +func (r RestoreResult) Extract() (*Restore, error) { + var s Restore + err := r.ExtractInto(&s) + return &s, err +} + +func (r RestoreResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "restore") +} + +// ExportResult contains the response body and error from an export request. +type ExportResult struct { + commonResult +} + +// BackupRecord contains an information about a backup backend storage. +type BackupRecord struct { + // The service used to perform the backup. + BackupService string `json:"backup_service"` + + // An identifier string to locate the backup. + BackupURL []byte `json:"backup_url"` +} + +// Extract will get the Backup record object out of the ExportResult object. +func (r ExportResult) Extract() (*BackupRecord, error) { + var s BackupRecord + err := r.ExtractInto(&s) + return &s, err +} + +func (r ExportResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "backup-record") +} + +// ImportResponse struct contains the response of the Backup Import action. +type ImportResponse struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// ImportResult contains the response body and error from an import request. +type ImportResult struct { + gophercloud.Result +} + +// Extract will get the Backup object out of the commonResult object. +func (r ImportResult) Extract() (*ImportResponse, error) { + var s ImportResponse + err := r.ExtractInto(&s) + return &s, err +} + +func (r ImportResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "backup") +} + +// ImportBackup contains all the information to import a Cinder Backup. +type ImportBackup struct { + ID string `json:"id"` + CreatedAt time.Time `json:"-"` + UpdatedAt time.Time `json:"-"` + VolumeID string `json:"volume_id"` + SnapshotID *string `json:"snapshot_id"` + Status *string `json:"status"` + Size *int `json:"size"` + ObjectCount *int `json:"object_count"` + Container *string `json:"container"` + ServiceMetadata *string `json:"service_metadata"` + Service *string `json:"service"` + Host *string `json:"host"` + UserID string `json:"user_id"` + DeletedAt time.Time `json:"-"` + DataTimestamp time.Time `json:"-"` + TempSnapshotID *string `json:"temp_snapshot_id"` + TempVolumeID *string `json:"temp_volume_id"` + RestoreVolumeID *string `json:"restore_volume_id"` + NumDependentBackups *int `json:"num_dependent_backups"` + EncryptionKeyID *string `json:"encryption_key_id"` + ParentID *string `json:"parent_id"` + Deleted bool `json:"deleted"` + DisplayName *string `json:"display_name"` + DisplayDescription *string `json:"display_description"` + DriverInfo interface{} `json:"driver_info"` + FailReason *string `json:"fail_reason"` + ProjectID string `json:"project_id"` + Metadata map[string]string `json:"metadata"` + AvailabilityZone *string `json:"availability_zone"` +} + +// UnmarshalJSON converts our JSON API response into our backup struct +func (r *ImportBackup) UnmarshalJSON(b []byte) error { + type tmp ImportBackup + var s struct { + tmp + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt time.Time `json:"deleted_at"` + DataTimestamp time.Time `json:"data_timestamp"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = ImportBackup(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.DeletedAt = time.Time(s.DeletedAt) + r.DataTimestamp = time.Time(s.DataTimestamp) + + return err +} + +// MarshalJSON converts our struct request into JSON backup import request +func (r ImportBackup) MarshalJSON() ([]byte, error) { + type b ImportBackup + type ext struct { + CreatedAt *string `json:"created_at"` + UpdatedAt *string `json:"updated_at"` + DeletedAt *string `json:"deleted_at"` + DataTimestamp *string `json:"data_timestamp"` + } + type tmp struct { + b + ext + } + + var t ext + if r.CreatedAt != (time.Time{}) { + v := r.CreatedAt.Format(time.RFC3339) + t.CreatedAt = &v + } + if r.UpdatedAt != (time.Time{}) { + v := r.UpdatedAt.Format(time.RFC3339) + t.UpdatedAt = &v + } + if r.DeletedAt != (time.Time{}) { + v := r.DeletedAt.Format(time.RFC3339) + t.DeletedAt = &v + } + if r.DataTimestamp != (time.Time{}) { + v := r.DataTimestamp.Format(time.RFC3339) + t.DataTimestamp = &v + } + + if r.Metadata == nil { + r.Metadata = make(map[string]string) + } + + s := tmp{ + b(r), + t, + } + + return json.Marshal(s) +} diff --git a/openstack/evs/extensions/backups/testing/fixtures.go b/openstack/evs/extensions/backups/testing/fixtures.go new file mode 100644 index 000000000..94cd9b52f --- /dev/null +++ b/openstack/evs/extensions/backups/testing/fixtures.go @@ -0,0 +1,300 @@ +package testing + +import ( + "encoding/json" + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ListResponse = ` +{ + "backups": [ + { + "id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "name": "backup-001" + }, + { + "id": "96c3bda7-c82a-4f50-be73-ca7621794835", + "name": "backup-002" + } + ], + "backups_links": [ + { + "href": "%s/backups?marker=1", + "rel": "next" + } + ] +} +` + +const ListDetailResponse = ` +{ + "backups": [ + { + "id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "name": "backup-001", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "description": "Daily Backup", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + }, + { + "id": "96c3bda7-c82a-4f50-be73-ca7621794835", + "name": "backup-002", + "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", + "description": "Weekly Backup", + "status": "available", + "size": 25, + "created_at": "2017-05-30T03:35:03.000000" + } + ], + "backups_links": [ + { + "href": "%s/backups/detail?marker=1", + "rel": "next" + } + ] +} +` + +const GetResponse = ` +{ + "backup": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "backup-001", + "description": "Daily backup", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + } +} +` +const CreateRequest = ` +{ + "backup": { + "volume_id": "1234", + "name": "backup-001" + } +} +` + +const CreateResponse = ` +{ + "backup": { + "volume_id": "1234", + "name": "backup-001", + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "description": "Daily backup", + "volume_id": "1234", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + } +} +` + +const RestoreRequest = ` +{ + "restore": { + "name": "vol-001", + "volume_id": "1234" + } +} +` + +const RestoreResponse = ` +{ + "restore": { + "backup_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "volume_id": "1234", + "volume_name": "vol-001" + } +} +` + +const ExportResponse = ` +{ + "backup-record": { + "backup_service": "cinder.backup.drivers.swift.SwiftBackupDriver", + "backup_url": "eyJpZCI6ImQzMjAxOWQzLWJjNmUtNDMxOS05YzFkLTY3MjJmYzEzNmEyMiIsInZvbHVtZV9pZCI6ImNmOWJjNmZhLWM1YmMtNDFmNi1iYzRlLTZlNzZjMGJlYTk1OSIsInNuYXBzaG90X2lkIjpudWxsLCJzdGF0dXMiOiJhdmFpbGFibGUiLCJzaXplIjoxLCJvYmplY3RfY291bnQiOjIsImNvbnRhaW5lciI6Im15LXRlc3QtYmFja3VwIiwic2VydmljZV9tZXRhZGF0YSI6InZvbHVtZV9jZjliYzZmYS1jNWJjLTQxZjYtYmM0ZS02ZTc2YzBiZWE5NTkvMjAyMDAzMTExOTI4NTUvYXpfcmVnaW9uYl9iYWNrdXBfYjg3YmIxZTUtMGQ0ZS00NDVlLWE1NDgtNWFlNzQyNTYyYmFjIiwic2VydmljZSI6ImNpbmRlci5iYWNrdXAuZHJpdmVycy5zd2lmdC5Td2lmdEJhY2t1cERyaXZlciIsImhvc3QiOiJjaW5kZXItYmFja3VwLWhvc3QxIiwidXNlcl9pZCI6IjkzNTE0ZTA0LWEwMjYtNGY2MC04MTc2LTM5NWM4NTk1MDFkZCIsInRlbXBfc25hcHNob3RfaWQiOm51bGwsInRlbXBfdm9sdW1lX2lkIjpudWxsLCJyZXN0b3JlX3ZvbHVtZV9pZCI6bnVsbCwibnVtX2RlcGVuZGVudF9iYWNrdXBzIjpudWxsLCJlbmNyeXB0aW9uX2tleV9pZCI6bnVsbCwicGFyZW50X2lkIjpudWxsLCJkZWxldGVkIjpmYWxzZSwiZGlzcGxheV9uYW1lIjpudWxsLCJkaXNwbGF5X2Rlc2NyaXB0aW9uIjpudWxsLCJkcml2ZXJfaW5mbyI6bnVsbCwiZmFpbF9yZWFzb24iOm51bGwsInByb2plY3RfaWQiOiIxNGYxYzFmNWQxMmI0NzU1Yjk0ZWRlZjc4ZmY4YjMyNSIsIm1ldGFkYXRhIjp7fSwiYXZhaWxhYmlsaXR5X3pvbmUiOiJyZWdpb24xYiIsImNyZWF0ZWRfYXQiOiIyMDIwLTAzLTExVDE5OjI1OjI0WiIsInVwZGF0ZWRfYXQiOiIyMDIwLTAzLTExVDE5OjI5OjA4WiIsImRlbGV0ZWRfYXQiOm51bGwsImRhdGFfdGltZXN0YW1wIjoiMjAyMC0wMy0xMVQxOToyNToyNFoifQ==" + } +} +` + +const ImportRequest = ExportResponse + +const ImportResponse = ` +{ + "backup": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "links": [ + { + "href": "https://volume/v2/14f1c1f5d12b4755b94edef78ff8b325/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", + "rel": "self" + }, + { + "href": "https://volume/14f1c1f5d12b4755b94edef78ff8b325/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", + "rel": "bookmark" + } + ], + "name": null + } +} +` + +var ( + status = "available" + availabilityZone = "region1b" + host = "cinder-backup-host1" + serviceMetadata = "volume_cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959/20200311192855/az_regionb_backup_b87bb1e5-0d4e-445e-a548-5ae742562bac" + size = 1 + objectCount = 2 + container = "my-test-backup" + service = "cinder.backup.drivers.swift.SwiftBackupDriver" + backupImport = backups.ImportBackup{ + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + Status: &status, + AvailabilityZone: &availabilityZone, + VolumeID: "cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959", + UpdatedAt: time.Date(2020, 3, 11, 19, 29, 8, 0, time.UTC), + Host: &host, + UserID: "93514e04-a026-4f60-8176-395c859501dd", + ServiceMetadata: &serviceMetadata, + Size: &size, + ObjectCount: &objectCount, + Container: &container, + Service: &service, + CreatedAt: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + DataTimestamp: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + ProjectID: "14f1c1f5d12b4755b94edef78ff8b325", + Metadata: make(map[string]string), + } + backupURL, _ = json.Marshal(backupImport) +) + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/backups", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ListResponse, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"backups": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +func MockListDetailResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/detail", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ListDetailResponse, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"backups": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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, GetResponse) + }) +} + +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/backups", 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.StatusAccepted) + + fmt.Fprintf(w, CreateResponse) + }) +} + +func MockRestoreResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22/restore", 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, RestoreRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, RestoreResponse) + }) +} + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) +} + +func MockExportResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22/export_record", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ExportResponse) + }) +} + +func MockImportResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/import_record", 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, ImportRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ImportResponse) + }) +} diff --git a/openstack/evs/extensions/backups/testing/requests_test.go b/openstack/evs/extensions/backups/testing/requests_test.go new file mode 100644 index 000000000..1adc7a451 --- /dev/null +++ b/openstack/evs/extensions/backups/testing/requests_test.go @@ -0,0 +1,189 @@ +package testing + +import ( + "encoding/json" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + count := 0 + + err := backups.List(client.ServiceClient(), &backups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := backups.ExtractBackups(page) + if err != nil { + t.Errorf("Failed to extract backups: %v", err) + return false, err + } + + expected := []backups.Backup{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "backup-001", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "backup-002", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + if err != nil { + t.Errorf("EachPage returned error: %s", err) + } + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestListDetail(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListDetailResponse(t) + + count := 0 + + err := backups.ListDetail(client.ServiceClient(), &backups.ListDetailOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := backups.ExtractBackups(page) + if err != nil { + t.Errorf("Failed to extract backups: %v", err) + return false, err + } + + expected := []backups.Backup{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "backup-001", + VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + Status: "available", + Size: 30, + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), + Description: "Daily Backup", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "backup-002", + VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", + Status: "available", + Size: 25, + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), + Description: "Weekly Backup", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + if err != nil { + t.Errorf("EachPage returned error: %s", err) + } + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + v, err := backups.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, v.Name, "backup-001") + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := backups.CreateOpts{VolumeID: "1234", Name: "backup-001"} + n, err := backups.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.VolumeID, "1234") + th.AssertEquals(t, n.Name, "backup-001") + th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestRestore(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockRestoreResponse(t) + + options := backups.RestoreOpts{VolumeID: "1234", Name: "vol-001"} + n, err := backups.RestoreFromBackup(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.VolumeID, "1234") + th.AssertEquals(t, n.VolumeName, "vol-001") + th.AssertEquals(t, n.BackupID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := backups.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} + +func TestExport(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockExportResponse(t) + + n, err := backups.Export(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.BackupService, "cinder.backup.drivers.swift.SwiftBackupDriver") + th.AssertDeepEquals(t, n.BackupURL, backupURL) + + tmp := backups.ImportBackup{} + err = json.Unmarshal(backupURL, &tmp) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, tmp, backupImport) +} + +func TestImport(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockImportResponse(t) + + options := backups.ImportOpts{ + BackupService: "cinder.backup.drivers.swift.SwiftBackupDriver", + BackupURL: backupURL, + } + n, err := backups.Import(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} diff --git a/openstack/evs/extensions/backups/urls.go b/openstack/evs/extensions/backups/urls.go new file mode 100644 index 000000000..e727de727 --- /dev/null +++ b/openstack/evs/extensions/backups/urls.go @@ -0,0 +1,39 @@ +package backups + +import "github.com/gophercloud/gophercloud" + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id) +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups") +} + +func listDetailURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups", "detail") +} + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id) +} + +func restoreURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id, "restore") +} + +func exportURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id, "export_record") +} + +func importURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups", "import_record") +} diff --git a/openstack/evs/extensions/limits/doc.go b/openstack/evs/extensions/limits/doc.go new file mode 100644 index 000000000..d33105884 --- /dev/null +++ b/openstack/evs/extensions/limits/doc.go @@ -0,0 +1,13 @@ +/* +Package limits shows rate and limit information for a project you authorized for. + +Example to Retrieve Limits + + limits, err := limits.Get(blockStorageClient).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", limits) +*/ +package limits diff --git a/openstack/evs/extensions/limits/requests.go b/openstack/evs/extensions/limits/requests.go new file mode 100644 index 000000000..21721f1bc --- /dev/null +++ b/openstack/evs/extensions/limits/requests.go @@ -0,0 +1,13 @@ +package limits + +import ( + "github.com/gophercloud/gophercloud" +) + +// Get returns the limits about the currently scoped tenant. +func Get(client *gophercloud.ServiceClient) (r GetResult) { + url := getURL(client) + resp, err := client.Get(url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/extensions/limits/results.go b/openstack/evs/extensions/limits/results.go new file mode 100644 index 000000000..f0a74b7ec --- /dev/null +++ b/openstack/evs/extensions/limits/results.go @@ -0,0 +1,80 @@ +package limits + +import ( + "github.com/gophercloud/gophercloud" +) + +// Limits is a struct that contains the response of a limit query. +type Limits struct { + // Absolute contains the limits and usage information. + // An absolute limit value of -1 indicates that the absolute limit for the item is infinite. + Absolute Absolute `json:"absolute"` + // Rate contains rate-limit volume copy bandwidth, used to mitigate slow down of data access from the instances. + Rate []Rate `json:"rate"` +} + +// Absolute is a struct that contains the current resource usage and limits +// of a project. +type Absolute struct { + // MaxTotalVolumes is the maximum number of volumes. + MaxTotalVolumes int `json:"maxTotalVolumes"` + + // MaxTotalSnapshots is the maximum number of snapshots. + MaxTotalSnapshots int `json:"maxTotalSnapshots"` + + // MaxTotalVolumeGigabytes is the maximum total amount of volumes, in gibibytes (GiB). + MaxTotalVolumeGigabytes int `json:"maxTotalVolumeGigabytes"` + + // MaxTotalBackups is the maximum number of backups. + MaxTotalBackups int `json:"maxTotalBackups"` + + // MaxTotalBackupGigabytes is the maximum total amount of backups, in gibibytes (GiB). + MaxTotalBackupGigabytes int `json:"maxTotalBackupGigabytes"` + + // TotalVolumesUsed is the total number of volumes used. + TotalVolumesUsed int `json:"totalVolumesUsed"` + + // TotalGigabytesUsed is the total number of gibibytes (GiB) used. + TotalGigabytesUsed int `json:"totalGigabytesUsed"` + + // TotalSnapshotsUsed the total number of snapshots used. + TotalSnapshotsUsed int `json:"totalSnapshotsUsed"` + + // TotalBackupsUsed is the total number of backups used. + TotalBackupsUsed int `json:"totalBackupsUsed"` + + // TotalBackupGigabytesUsed is the total number of backups gibibytes (GiB) used. + TotalBackupGigabytesUsed int `json:"totalBackupGigabytesUsed"` +} + +// Rate is a struct that contains the +// rate-limit volume copy bandwidth, used to mitigate slow down of data access from the instances. +type Rate struct { + Regex string `json:"regex"` + URI string `json:"uri"` + Limit []Limit `json:"limit"` +} + +// Limit struct contains Limit values for the Rate struct +type Limit struct { + Verb string `json:"verb"` + NextAvailable string `json:"next-available"` + Unit string `json:"unit"` + Value int `json:"value"` + Remaining int `json:"remaining"` +} + +// Extract interprets a limits result as a Limits. +func (r GetResult) Extract() (*Limits, error) { + var s struct { + Limits *Limits `json:"limits"` + } + err := r.ExtractInto(&s) + return s.Limits, err +} + +// GetResult is the response from a Get operation. Call its Extract +// method to interpret it as an Absolute. +type GetResult struct { + gophercloud.Result +} diff --git a/openstack/evs/extensions/limits/testing/fixtures.go b/openstack/evs/extensions/limits/testing/fixtures.go new file mode 100644 index 000000000..2df756eff --- /dev/null +++ b/openstack/evs/extensions/limits/testing/fixtures.go @@ -0,0 +1,129 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/limits" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// GetOutput is a sample response to a Get call. +const GetOutput = ` +{ + "limits": { + "rate": [ + { + "regex": ".*", + "uri": "*", + "limit": [ + { + "verb": "GET", + "next-available": "1970-01-01T00:00:00", + "unit": "MINUTE", + "value": 10, + "remaining": 10 + }, + { + "verb": "POST", + "next-available": "1970-01-01T00:00:00", + "unit": "HOUR", + "value": 5, + "remaining": 5 + } + ] + }, + { + "regex": "changes-since", + "uri": "changes-since*", + "limit": [ + { + "verb": "GET", + "next-available": "1970-01-01T00:00:00", + "unit": "MINUTE", + "value": 5, + "remaining": 5 + } + ] + } + ], + "absolute": { + "maxTotalVolumes": 40, + "maxTotalSnapshots": 40, + "maxTotalVolumeGigabytes": 1000, + "maxTotalBackups": 10, + "maxTotalBackupGigabytes": 1000, + "totalVolumesUsed": 1, + "totalGigabytesUsed": 100, + "totalSnapshotsUsed": 1, + "totalBackupsUsed": 1, + "totalBackupGigabytesUsed": 50 + } + } +} +` + +// LimitsResult is the result of the limits in GetOutput. +var LimitsResult = limits.Limits{ + Rate: []limits.Rate{ + { + Regex: ".*", + URI: "*", + Limit: []limits.Limit{ + { + Verb: "GET", + NextAvailable: "1970-01-01T00:00:00", + Unit: "MINUTE", + Value: 10, + Remaining: 10, + }, + { + Verb: "POST", + NextAvailable: "1970-01-01T00:00:00", + Unit: "HOUR", + Value: 5, + Remaining: 5, + }, + }, + }, + { + Regex: "changes-since", + URI: "changes-since*", + Limit: []limits.Limit{ + { + Verb: "GET", + NextAvailable: "1970-01-01T00:00:00", + Unit: "MINUTE", + Value: 5, + Remaining: 5, + }, + }, + }, + }, + Absolute: limits.Absolute{ + MaxTotalVolumes: 40, + MaxTotalSnapshots: 40, + MaxTotalVolumeGigabytes: 1000, + MaxTotalBackups: 10, + MaxTotalBackupGigabytes: 1000, + TotalVolumesUsed: 1, + TotalGigabytesUsed: 100, + TotalSnapshotsUsed: 1, + TotalBackupsUsed: 1, + TotalBackupGigabytesUsed: 50, + }, +} + +// HandleGetSuccessfully configures the test server to respond to a Get request +// for a limit. +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/limits", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, GetOutput) + }) +} diff --git a/openstack/evs/extensions/limits/testing/requests_test.go b/openstack/evs/extensions/limits/testing/requests_test.go new file mode 100644 index 000000000..b1a59673c --- /dev/null +++ b/openstack/evs/extensions/limits/testing/requests_test.go @@ -0,0 +1,19 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/limits" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t) + + actual, err := limits.Get(client.ServiceClient()).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &LimitsResult, actual) +} diff --git a/openstack/evs/extensions/limits/urls.go b/openstack/evs/extensions/limits/urls.go new file mode 100644 index 000000000..edd97e4e0 --- /dev/null +++ b/openstack/evs/extensions/limits/urls.go @@ -0,0 +1,11 @@ +package limits + +import ( + "github.com/gophercloud/gophercloud" +) + +const resourcePath = "limits" + +func getURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} diff --git a/openstack/evs/extensions/quotasets/delete.go b/openstack/evs/extensions/quotasets/delete.go deleted file mode 100644 index 1ccb8edc6..000000000 --- a/openstack/evs/extensions/quotasets/delete.go +++ /dev/null @@ -1,10 +0,0 @@ -package quotasets - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func Delete(client *golangsdk.ServiceClient, projectID string) (err error) { - _, err = client.Delete(client.ServiceURL("os-quota-sets", projectID), &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - return -} diff --git a/openstack/evs/extensions/quotasets/doc.go b/openstack/evs/extensions/quotasets/doc.go new file mode 100644 index 000000000..a60f953d0 --- /dev/null +++ b/openstack/evs/extensions/quotasets/doc.go @@ -0,0 +1,60 @@ +/* +Package quotasets enables retrieving and managing Block Storage quotas. + +Example to Get a Quota Set + + quotaset, err := quotasets.Get(blockStorageClient, "project-id").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Get Quota Set Usage + + quotaset, err := quotasets.GetUsage(blockStorageClient, "project-id").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Update a Quota Set + + updateOpts := quotasets.UpdateOpts{ + Volumes: gophercloud.IntToPointer(100), + } + + quotaset, err := quotasets.Update(blockStorageClient, "project-id", updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Update a Quota set with volume_type quotas + + updateOpts := quotasets.UpdateOpts{ + Volumes: gophercloud.IntToPointer(100), + Extra: map[string]interface{}{ + "gigabytes_foo": gophercloud.IntToPointer(100), + "snapshots_foo": gophercloud.IntToPointer(10), + "volumes_foo": gophercloud.IntToPointer(10), + }, + } + + quotaset, err := quotasets.Update(blockStorageClient, "project-id", updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Delete a Quota Set + + err := quotasets.Delete(blockStorageClient, "project-id").ExtractErr() + if err != nil { + panic(err) + } +*/ +package quotasets diff --git a/openstack/evs/extensions/quotasets/get.go b/openstack/evs/extensions/quotasets/get.go deleted file mode 100644 index e95b03b7b..000000000 --- a/openstack/evs/extensions/quotasets/get.go +++ /dev/null @@ -1,50 +0,0 @@ -package quotasets - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" -) - -func Get(client *golangsdk.ServiceClient, projectID string) (*QuotaSet, error) { - raw, err := client.Get(client.ServiceURL("os-quota-sets", projectID), nil, nil) - if err != nil { - return nil, err - } - - var res struct { - QuotaSet QuotaSet `json:"quota_set"` - } - err = extract.Into(raw.Body, &res) - return &res.QuotaSet, err -} - -func GetDefaults(client *golangsdk.ServiceClient, projectID string) (*QuotaSet, error) { - raw, err := client.Get(client.ServiceURL("os-quota-sets", projectID, "defaults"), nil, nil) - if err != nil { - return nil, err - } - - var res QuotaSet - err = extract.IntoStructPtr(raw.Body, &res, "quota_set") - return &res, err -} - -type QuotaSet struct { - // ID is project associated with this QuotaSet. - ID string `json:"id"` - // Volumes is the number of volumes that are allowed for each project. - Volumes int `json:"volumes"` - // Snapshots is the number of snapshots that are allowed for each project. - Snapshots int `json:"snapshots"` - // Gigabytes is the size (GB) of volumes and snapshots that are allowed for - // each project. - Gigabytes int `json:"gigabytes"` - // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are - // allowed for each project and the specifed volume type. - PerVolumeGigabytes int `json:"per_volume_gigabytes"` - // Backups is the number of backups that are allowed for each project. - Backups int `json:"backups"` - // BackupGigabytes is the size (GB) of backups that are allowed for each - // project. - BackupGigabytes int `json:"backup_gigabytes"` -} diff --git a/openstack/evs/extensions/quotasets/get_usage.go b/openstack/evs/extensions/quotasets/get_usage.go deleted file mode 100644 index 4cfb66e89..000000000 --- a/openstack/evs/extensions/quotasets/get_usage.go +++ /dev/null @@ -1,65 +0,0 @@ -package quotasets - -import ( - "fmt" - - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" -) - -func GetUsage(client *golangsdk.ServiceClient, projectID string) (*QuotaUsageSet, error) { - raw, err := client.Get(fmt.Sprintf("%s?usage=true", client.ServiceURL("os-quota-sets", projectID)), nil, nil) - if err != nil { - return nil, err - } - - var res QuotaUsageSet - err = extract.IntoStructPtr(raw.Body, &res, "quota_set") - return &res, err -} - -type QuotaUsageSet struct { - // ID is the project ID associated with this QuotaUsageSet. - ID string `json:"id"` - // Volumes is the volume usage information for this project, including - // in_use, limit, reserved and allocated attributes. Note: allocated - // attribute is available only when nested quota is enabled. - Volumes QuotaUsage `json:"volumes"` - // Snapshots is the snapshot usage information for this project, including - // in_use, limit, reserved and allocated attributes. Note: allocated - // attribute is available only when nested quota is enabled. - Snapshots QuotaUsage `json:"snapshots"` - // Gigabytes is the size (GB) usage information of volumes and snapshots - // for this project, including in_use, limit, reserved and allocated - // attributes. Note: allocated attribute is available only when nested - // quota is enabled. - Gigabytes QuotaUsage `json:"gigabytes"` - // PerVolumeGigabytes is the size (GB) usage information for each volume, - // including in_use, limit, reserved and allocated attributes. Note: - // allocated attribute is available only when nested quota is enabled and - // only limit is meaningful here. - PerVolumeGigabytes QuotaUsage `json:"per_volume_gigabytes"` - // Backups is the backup usage information for this project, including - // in_use, limit, reserved and allocated attributes. Note: allocated - // attribute is available only when nested quota is enabled. - Backups QuotaUsage `json:"backups"` - // BackupGigabytes is the size (GB) usage information of backup for this - // project, including in_use, limit, reserved and allocated attributes. - // Note: allocated attribute is available only when nested quota is - // enabled. - BackupGigabytes QuotaUsage `json:"backup_gigabytes"` -} - -type QuotaUsage struct { - // InUse is the current number of provisioned resources of the given type. - InUse int `json:"in_use"` - // Allocated is the current number of resources of a given type allocated - // for use. It is only available when nested quota is enabled. - Allocated int `json:"allocated"` - // Reserved is a transitional state when a claim against quota has been made - // but the resource is not yet fully online. - Reserved int `json:"reserved"` - // Limit is the maximum number of a given resource that can be - // allocated/provisioned. This is what "quota" usually refers to. - Limit int `json:"limit"` -} diff --git a/openstack/evs/extensions/quotasets/requests.go b/openstack/evs/extensions/quotasets/requests.go new file mode 100644 index 000000000..5cf28aee9 --- /dev/null +++ b/openstack/evs/extensions/quotasets/requests.go @@ -0,0 +1,116 @@ +package quotasets + +import ( + "fmt" + + "github.com/gophercloud/gophercloud" +) + +// Get returns public data about a previously created QuotaSet. +func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { + resp, err := client.Get(getURL(client, projectID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetDefaults returns public data about the project's default block storage quotas. +func GetDefaults(client *gophercloud.ServiceClient, projectID string) (r GetResult) { + resp, err := client.Get(getDefaultsURL(client, projectID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetUsage returns detailed public data about a previously created QuotaSet. +func GetUsage(client *gophercloud.ServiceClient, projectID string) (r GetUsageResult) { + u := fmt.Sprintf("%s?usage=true", getURL(client, projectID)) + resp, err := client.Get(u, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Updates the quotas for the given projectID and returns the new QuotaSet. +func Update(client *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToBlockStorageQuotaUpdateMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Put(updateURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder enables extensions to add parameters to the update request. +type UpdateOptsBuilder interface { + // Extra specific name to prevent collisions with interfaces for other quotas + // (e.g. neutron) + ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) +} + +// ToBlockStorageQuotaUpdateMap builds the update options into a serializable +// format. +func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "quota_set") + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["quota_set"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil +} + +// Options for Updating the quotas of a Tenant. +// All int-values are pointers so they can be nil if they are not needed. +// You can use gopercloud.IntToPointer() for convenience +type UpdateOpts struct { + // Volumes is the number of volumes that are allowed for each project. + Volumes *int `json:"volumes,omitempty"` + + // Snapshots is the number of snapshots that are allowed for each project. + Snapshots *int `json:"snapshots,omitempty"` + + // Gigabytes is the size (GB) of volumes and snapshots that are allowed for + // each project. + Gigabytes *int `json:"gigabytes,omitempty"` + + // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are + // allowed for each project and the specifed volume type. + PerVolumeGigabytes *int `json:"per_volume_gigabytes,omitempty"` + + // Backups is the number of backups that are allowed for each project. + Backups *int `json:"backups,omitempty"` + + // BackupGigabytes is the size (GB) of backups that are allowed for each + // project. + BackupGigabytes *int `json:"backup_gigabytes,omitempty"` + + // Groups is the number of groups that are allowed for each project. + Groups *int `json:"groups,omitempty"` + + // Force will update the quotaset even if the quota has already been used + // and the reserved quota exceeds the new quota. + Force bool `json:"force,omitempty"` + + // Extra is a collection of miscellaneous key/values used to set + // quota per volume_type + Extra map[string]interface{} `json:"-"` +} + +// Resets the quotas for the given tenant to their default values. +func Delete(client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { + resp, err := client.Delete(updateURL(client, projectID), &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/extensions/quotasets/results.go b/openstack/evs/extensions/quotasets/results.go new file mode 100644 index 000000000..bc516be5d --- /dev/null +++ b/openstack/evs/extensions/quotasets/results.go @@ -0,0 +1,205 @@ +package quotasets + +import ( + "encoding/json" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// QuotaSet is a set of operational limits that allow for control of block +// storage usage. +type QuotaSet struct { + // ID is project associated with this QuotaSet. + ID string `json:"id"` + + // Volumes is the number of volumes that are allowed for each project. + Volumes int `json:"volumes"` + + // Snapshots is the number of snapshots that are allowed for each project. + Snapshots int `json:"snapshots"` + + // Gigabytes is the size (GB) of volumes and snapshots that are allowed for + // each project. + Gigabytes int `json:"gigabytes"` + + // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are + // allowed for each project and the specifed volume type. + PerVolumeGigabytes int `json:"per_volume_gigabytes"` + + // Backups is the number of backups that are allowed for each project. + Backups int `json:"backups"` + + // BackupGigabytes is the size (GB) of backups that are allowed for each + // project. + BackupGigabytes int `json:"backup_gigabytes"` + + // Groups is the number of groups that are allowed for each project. + Groups int `json:"groups,omitempty"` + + // Extra is a collection of miscellaneous key/values used to set + // quota per volume_type + Extra map[string]interface{} `json:"-"` +} + +// UnmarshalJSON is used on QuotaSet to unmarshal extra keys that are +// used for volume_type quota +func (r *QuotaSet) UnmarshalJSON(b []byte) error { + type tmp QuotaSet + var s struct { + tmp + Extra map[string]interface{} `json:"extra"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = QuotaSet(s.tmp) + + var result interface{} + err = json.Unmarshal(b, &result) + if err != nil { + return err + } + if resultMap, ok := result.(map[string]interface{}); ok { + r.Extra = gophercloud.RemainingKeys(QuotaSet{}, resultMap) + } + + return err +} + +// QuotaUsageSet represents details of both operational limits of block +// storage resources and the current usage of those resources. +type QuotaUsageSet struct { + // ID is the project ID associated with this QuotaUsageSet. + ID string `json:"id"` + + // Volumes is the volume usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Volumes QuotaUsage `json:"volumes"` + + // Snapshots is the snapshot usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Snapshots QuotaUsage `json:"snapshots"` + + // Gigabytes is the size (GB) usage information of volumes and snapshots + // for this project, including in_use, limit, reserved and allocated + // attributes. Note: allocated attribute is available only when nested + // quota is enabled. + Gigabytes QuotaUsage `json:"gigabytes"` + + // PerVolumeGigabytes is the size (GB) usage information for each volume, + // including in_use, limit, reserved and allocated attributes. Note: + // allocated attribute is available only when nested quota is enabled and + // only limit is meaningful here. + PerVolumeGigabytes QuotaUsage `json:"per_volume_gigabytes"` + + // Backups is the backup usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Backups QuotaUsage `json:"backups"` + + // BackupGigabytes is the size (GB) usage information of backup for this + // project, including in_use, limit, reserved and allocated attributes. + // Note: allocated attribute is available only when nested quota is + // enabled. + BackupGigabytes QuotaUsage `json:"backup_gigabytes"` + + // Groups is the number of groups that are allowed for each project. + // Note: allocated attribute is available only when nested quota is + // enabled. + Groups QuotaUsage `json:"groups"` +} + +// QuotaUsage is a set of details about a single operational limit that allows +// for control of block storage usage. +type QuotaUsage struct { + // InUse is the current number of provisioned resources of the given type. + InUse int `json:"in_use"` + + // Allocated is the current number of resources of a given type allocated + // for use. It is only available when nested quota is enabled. + Allocated int `json:"allocated"` + + // Reserved is a transitional state when a claim against quota has been made + // but the resource is not yet fully online. + Reserved int `json:"reserved"` + + // Limit is the maximum number of a given resource that can be + // allocated/provisioned. This is what "quota" usually refers to. + Limit int `json:"limit"` +} + +// QuotaSetPage stores a single page of all QuotaSet results from a List call. +type QuotaSetPage struct { + pagination.SinglePageBase +} + +// IsEmpty determines whether or not a QuotaSetsetPage is empty. +func (r QuotaSetPage) IsEmpty() (bool, error) { + ks, err := ExtractQuotaSets(r) + return len(ks) == 0, err +} + +// ExtractQuotaSets interprets a page of results as a slice of QuotaSets. +func ExtractQuotaSets(r pagination.Page) ([]QuotaSet, error) { + var s struct { + QuotaSets []QuotaSet `json:"quotas"` + } + err := (r.(QuotaSetPage)).ExtractInto(&s) + return s.QuotaSets, err +} + +type quotaResult struct { + gophercloud.Result +} + +// Extract is a method that attempts to interpret any QuotaSet resource response +// as a QuotaSet struct. +func (r quotaResult) Extract() (*QuotaSet, error) { + var s struct { + QuotaSet *QuotaSet `json:"quota_set"` + } + err := r.ExtractInto(&s) + return s.QuotaSet, err +} + +// GetResult is the response from a Get operation. Call its Extract method to +// interpret it as a QuotaSet. +type GetResult struct { + quotaResult +} + +// UpdateResult is the response from a Update operation. Call its Extract method +// to interpret it as a QuotaSet. +type UpdateResult struct { + quotaResult +} + +type quotaUsageResult struct { + gophercloud.Result +} + +// GetUsageResult is the response from a Get operation. Call its Extract +// method to interpret it as a QuotaSet. +type GetUsageResult struct { + quotaUsageResult +} + +// Extract is a method that attempts to interpret any QuotaUsageSet resource +// response as a set of QuotaUsageSet structs. +func (r quotaUsageResult) Extract() (QuotaUsageSet, error) { + var s struct { + QuotaUsageSet QuotaUsageSet `json:"quota_set"` + } + err := r.ExtractInto(&s) + return s.QuotaUsageSet, err +} + +// DeleteResult is the response from a Delete operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/evs/extensions/quotasets/testing/doc.go b/openstack/evs/extensions/quotasets/testing/doc.go new file mode 100644 index 000000000..30d864eb9 --- /dev/null +++ b/openstack/evs/extensions/quotasets/testing/doc.go @@ -0,0 +1,2 @@ +// quotasets unit tests +package testing diff --git a/openstack/evs/extensions/quotasets/testing/fixtures.go b/openstack/evs/extensions/quotasets/testing/fixtures.go index e7185bb8b..8c9f9318f 100644 --- a/openstack/evs/extensions/quotasets/testing/fixtures.go +++ b/openstack/evs/extensions/quotasets/testing/fixtures.go @@ -5,25 +5,25 @@ import ( "net/http" "testing" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/quotasets" - - "github.com/opentelekomcloud/gophertelekomcloud" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" ) const FirstTenantID = "555544443333222211110000ffffeeee" var getExpectedJSONBody = ` { - "quota_set" : { - "volumes" : 8, - "snapshots" : 9, - "gigabytes" : 10, - "per_volume_gigabytes" : 11, - "backups" : 12, - "backup_gigabytes" : 13 - } + "quota_set" : { + "volumes" : 8, + "snapshots" : 9, + "gigabytes" : 10, + "per_volume_gigabytes" : 11, + "backups" : 12, + "backup_gigabytes" : 13, + "groups": 14 + } }` var getExpectedQuotaSet = quotasets.QuotaSet{ @@ -33,45 +33,51 @@ var getExpectedQuotaSet = quotasets.QuotaSet{ PerVolumeGigabytes: 11, Backups: 12, BackupGigabytes: 13, + Groups: 14, + Extra: make(map[string]interface{}), } var getUsageExpectedJSONBody = ` { - "quota_set": { - "id": "555544443333222211110000ffffeeee", - "volumes": { - "in_use": 15, - "limit": 16, - "reserved": 17 - }, - "snapshots": { - "in_use": 18, - "limit": 19, - "reserved": 20 - }, - "gigabytes": { - "in_use": 21, - "limit": 22, - "reserved": 23 - }, - "per_volume_gigabytes": { - "in_use": 24, - "limit": 25, - "reserved": 26 - }, - "backups": { - "in_use": 27, - "limit": 28, - "reserved": 29 - }, - "backup_gigabytes": { - "in_use": 30, - "limit": 31, - "reserved": 32 - } - } -} -` + "quota_set" : { + "id": "555544443333222211110000ffffeeee", + "volumes" : { + "in_use": 15, + "limit": 16, + "reserved": 17 + }, + "snapshots" : { + "in_use": 18, + "limit": 19, + "reserved": 20 + }, + "gigabytes" : { + "in_use": 21, + "limit": 22, + "reserved": 23 + }, + "per_volume_gigabytes" : { + "in_use": 24, + "limit": 25, + "reserved": 26 + }, + "backups" : { + "in_use": 27, + "limit": 28, + "reserved": 29 + }, + "backup_gigabytes" : { + "in_use": 30, + "limit": 31, + "reserved": 32 + }, + "groups" : { + "in_use": 40, + "limit": 41, + "reserved": 42 + } + } +}` var getUsageExpectedQuotaSet = quotasets.QuotaUsageSet{ ID: FirstTenantID, @@ -81,27 +87,31 @@ var getUsageExpectedQuotaSet = quotasets.QuotaUsageSet{ PerVolumeGigabytes: quotasets.QuotaUsage{InUse: 24, Limit: 25, Reserved: 26}, Backups: quotasets.QuotaUsage{InUse: 27, Limit: 28, Reserved: 29}, BackupGigabytes: quotasets.QuotaUsage{InUse: 30, Limit: 31, Reserved: 32}, + Groups: quotasets.QuotaUsage{InUse: 40, Limit: 41, Reserved: 42}, } var fullUpdateExpectedJSONBody = ` { - "quota_set": { - "volumes": 8, - "snapshots": 9, - "gigabytes": 10, - "per_volume_gigabytes": 11, - "backups": 12, - "backup_gigabytes": 13 - } + "quota_set": { + "volumes": 8, + "snapshots": 9, + "gigabytes": 10, + "per_volume_gigabytes": 11, + "backups": 12, + "backup_gigabytes": 13, + "groups": 14 + } }` var fullUpdateOpts = quotasets.UpdateOpts{ - Volumes: golangsdk.IntToPointer(8), - Snapshots: golangsdk.IntToPointer(9), - Gigabytes: golangsdk.IntToPointer(10), - PerVolumeGigabytes: golangsdk.IntToPointer(11), - Backups: golangsdk.IntToPointer(12), - BackupGigabytes: golangsdk.IntToPointer(13), + Volumes: gophercloud.IntToPointer(8), + Snapshots: gophercloud.IntToPointer(9), + Gigabytes: gophercloud.IntToPointer(10), + PerVolumeGigabytes: gophercloud.IntToPointer(11), + Backups: gophercloud.IntToPointer(12), + BackupGigabytes: gophercloud.IntToPointer(13), + Groups: gophercloud.IntToPointer(14), + Extra: make(map[string]interface{}), } var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ @@ -111,30 +121,36 @@ var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ PerVolumeGigabytes: 11, Backups: 12, BackupGigabytes: 13, + Groups: 14, + Extra: make(map[string]interface{}), } var partialUpdateExpectedJSONBody = ` { - "quota_set": { - "volumes": 200, - "snapshots": 0, - "gigabytes": 0, - "per_volume_gigabytes": 0, - "backups": 0, - "backup_gigabytes": 0 - } + "quota_set": { + "volumes": 200, + "snapshots": 0, + "gigabytes": 0, + "per_volume_gigabytes": 0, + "backups": 0, + "backup_gigabytes": 0 + } }` var partialUpdateOpts = quotasets.UpdateOpts{ - Volumes: golangsdk.IntToPointer(200), - Snapshots: golangsdk.IntToPointer(0), - Gigabytes: golangsdk.IntToPointer(0), - PerVolumeGigabytes: golangsdk.IntToPointer(0), - Backups: golangsdk.IntToPointer(0), - BackupGigabytes: golangsdk.IntToPointer(0), + Volumes: gophercloud.IntToPointer(200), + Snapshots: gophercloud.IntToPointer(0), + Gigabytes: gophercloud.IntToPointer(0), + PerVolumeGigabytes: gophercloud.IntToPointer(0), + Backups: gophercloud.IntToPointer(0), + BackupGigabytes: gophercloud.IntToPointer(0), + Extra: make(map[string]interface{}), } -var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{Volumes: 200} +var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{ + Volumes: 200, + Extra: make(map[string]interface{}), +} // HandleSuccessfulRequest configures the test server to respond to an HTTP request. func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput string, uriQueryParams map[string]string) { @@ -148,7 +164,7 @@ func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput strin th.TestFormValues(t, r, uriQueryParams) } - _, _ = fmt.Fprint(w, jsonOutput) + fmt.Fprintf(w, jsonOutput) }) } diff --git a/openstack/evs/extensions/quotasets/testing/requests_test.go b/openstack/evs/extensions/quotasets/testing/requests_test.go index ccb67f398..922b8d336 100644 --- a/openstack/evs/extensions/quotasets/testing/requests_test.go +++ b/openstack/evs/extensions/quotasets/testing/requests_test.go @@ -1,12 +1,12 @@ package testing import ( + "errors" "testing" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/quotasets" - - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGet(t *testing.T) { @@ -15,7 +15,7 @@ func TestGet(t *testing.T) { uriQueryParms := map[string]string{} HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID) + actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &getExpectedQuotaSet, actual) } @@ -26,9 +26,9 @@ func TestGetUsage(t *testing.T) { uriQueryParms := map[string]string{"usage": "true"} HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getUsageExpectedJSONBody, uriQueryParms) - actual, err := quotasets.GetUsage(client.ServiceClient(), FirstTenantID) + actual, err := quotasets.GetUsage(client.ServiceClient(), FirstTenantID).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &getUsageExpectedQuotaSet, actual) + th.CheckDeepEquals(t, getUsageExpectedQuotaSet, actual) } func TestFullUpdate(t *testing.T) { @@ -37,7 +37,7 @@ func TestFullUpdate(t *testing.T) { uriQueryParms := map[string]string{} HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, fullUpdateExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, fullUpdateOpts) + actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, fullUpdateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &fullUpdateExpectedQuotaSet, actual) } @@ -48,16 +48,33 @@ func TestPartialUpdate(t *testing.T) { uriQueryParms := map[string]string{} HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, partialUpdateExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, partialUpdateOpts) + actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, partialUpdateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &partiualUpdateExpectedQuotaSet, actual) } +type ErrorUpdateOpts quotasets.UpdateOpts + +func (opts ErrorUpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { + return nil, errors.New("This is an error") +} + +func TestErrorInToBlockStorageQuotaUpdateMap(t *testing.T) { + opts := &ErrorUpdateOpts{} + th.SetupHTTP() + defer th.TeardownHTTP() + HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, "", nil) + _, err := quotasets.Update(client.ServiceClient(), FirstTenantID, opts).Extract() + if err == nil { + t.Fatal("Error handling failed") + } +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) - err := quotasets.Delete(client.ServiceClient(), FirstTenantID) + err := quotasets.Delete(client.ServiceClient(), FirstTenantID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/evs/extensions/quotasets/update.go b/openstack/evs/extensions/quotasets/update.go deleted file mode 100644 index aef568063..000000000 --- a/openstack/evs/extensions/quotasets/update.go +++ /dev/null @@ -1,47 +0,0 @@ -package quotasets - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" -) - -func Update(client *golangsdk.ServiceClient, projectID string, opts UpdateOpts) (*QuotaSet, error) { - b, err := golangsdk.BuildRequestBody(opts, "quota_set") - if err != nil { - return nil, err - } - - raw, err := client.Put(client.ServiceURL("os-quota-sets", projectID), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - if err != nil { - return nil, err - } - - var res QuotaSet - err = extract.IntoStructPtr(raw.Body, &res, "quota_set") - return &res, err -} - -type UpdateOpts struct { - // Volumes is the number of volumes that are allowed for each project. - Volumes *int `json:"volumes,omitempty"` - // Snapshots is the number of snapshots that are allowed for each project. - Snapshots *int `json:"snapshots,omitempty"` - // Gigabytes is the size (GB) of volumes and snapshots that are allowed for - // each project. - Gigabytes *int `json:"gigabytes,omitempty"` - // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are - // allowed for each project and the specifed volume type. - PerVolumeGigabytes *int `json:"per_volume_gigabytes,omitempty"` - // Backups is the number of backups that are allowed for each project. - Backups *int `json:"backups,omitempty"` - // BackupGigabytes is the size (GB) of backups that are allowed for each - // project. - BackupGigabytes *int `json:"backup_gigabytes,omitempty"` - // Groups is the number of groups that are allowed for each project. - Groups *int `json:"groups,omitempty"` - // Force will update the quotaset even if the quota has already been used - // and the reserved quota exceeds the new quota. - Force bool `json:"force,omitempty"` -} diff --git a/openstack/evs/extensions/quotasets/urls.go b/openstack/evs/extensions/quotasets/urls.go new file mode 100644 index 000000000..7d8e5ceb7 --- /dev/null +++ b/openstack/evs/extensions/quotasets/urls.go @@ -0,0 +1,21 @@ +package quotasets + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "os-quota-sets" + +func getURL(c *gophercloud.ServiceClient, projectID string) string { + return c.ServiceURL(resourcePath, projectID) +} + +func getDefaultsURL(c *gophercloud.ServiceClient, projectID string) string { + return c.ServiceURL(resourcePath, projectID, "defaults") +} + +func updateURL(c *gophercloud.ServiceClient, projectID string) string { + return getURL(c, projectID) +} + +func deleteURL(c *gophercloud.ServiceClient, projectID string) string { + return getURL(c, projectID) +} diff --git a/openstack/evs/extensions/schedulerhints/doc.go b/openstack/evs/extensions/schedulerhints/doc.go new file mode 100644 index 000000000..3a0d451b6 --- /dev/null +++ b/openstack/evs/extensions/schedulerhints/doc.go @@ -0,0 +1,52 @@ +/* +Package schedulerhints extends the volume create request with the ability to +specify additional parameters which determine where the volume will be +created in the OpenStack cloud. + +Example to Place Volume B on a Different Host than Volume A + + schedulerHints := schedulerhints.SchedulerHints{ + DifferentHost: []string{ + "volume-a-uuid", + } + } + + volumeCreateOpts := volumes.CreateOpts{ + Name: "volume_b", + Size: 10, + } + + createOpts := schedulerhints.CreateOptsExt{ + VolumeCreateOptsBuilder: volumeCreateOpts, + SchedulerHints: schedulerHints, + } + + volume, err := volumes.Create(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Place Volume B on the Same Host as Volume A + + schedulerHints := schedulerhints.SchedulerHints{ + SameHost: []string{ + "volume-a-uuid", + } + } + + volumeCreateOpts := volumes.CreateOpts{ + Name: "volume_b", + Size: 10 + } + + createOpts := schedulerhints.CreateOptsExt{ + VolumeCreateOptsBuilder: volumeCreateOpts, + SchedulerHints: schedulerHints, + } + + volume, err := volumes.Create(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } +*/ +package schedulerhints diff --git a/openstack/evs/extensions/schedulerhints/requests.go b/openstack/evs/extensions/schedulerhints/requests.go new file mode 100644 index 000000000..05b722da4 --- /dev/null +++ b/openstack/evs/extensions/schedulerhints/requests.go @@ -0,0 +1,124 @@ +package schedulerhints + +import ( + "regexp" + + "github.com/gophercloud/gophercloud" +) + +// SchedulerHints represents a set of scheduling hints that are passed to the +// OpenStack scheduler. +type SchedulerHints struct { + // DifferentHost will place the volume on a different back-end that does not + // host the given volumes. + DifferentHost []string + + // SameHost will place the volume on a back-end that hosts the given volumes. + SameHost []string + + // LocalToInstance will place volume on same host on a given instance + LocalToInstance string + + // Query is a conditional statement that results in back-ends able to + // host the volume. + Query string + + // AdditionalProperies are arbitrary key/values that are not validated by nova. + AdditionalProperties map[string]interface{} +} + +// VolumeCreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type VolumeCreateOptsBuilder interface { + ToVolumeCreateMap() (map[string]interface{}, error) +} + +// CreateOptsBuilder builds the scheduler hints into a serializable format. +type CreateOptsBuilder interface { + ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) +} + +// ToVolumeSchedulerHintsMap builds the scheduler hints into a serializable format. +func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) { + sh := make(map[string]interface{}) + + uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") + + if len(opts.DifferentHost) > 0 { + for _, diffHost := range opts.DifferentHost { + if !uuidRegex.MatchString(diffHost) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.DifferentHost" + err.Value = opts.DifferentHost + err.Info = "The hosts must be in UUID format." + return nil, err + } + } + sh["different_host"] = opts.DifferentHost + } + + if len(opts.SameHost) > 0 { + for _, sameHost := range opts.SameHost { + if !uuidRegex.MatchString(sameHost) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.SameHost" + err.Value = opts.SameHost + err.Info = "The hosts must be in UUID format." + return nil, err + } + } + sh["same_host"] = opts.SameHost + } + + if opts.LocalToInstance != "" { + if !uuidRegex.MatchString(opts.LocalToInstance) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.LocalToInstance" + err.Value = opts.LocalToInstance + err.Info = "The instance must be in UUID format." + return nil, err + } + sh["local_to_instance"] = opts.LocalToInstance + } + + if opts.Query != "" { + sh["query"] = opts.Query + } + + if opts.AdditionalProperties != nil { + for k, v := range opts.AdditionalProperties { + sh[k] = v + } + } + + return sh, nil +} + +// CreateOptsExt adds a SchedulerHints option to the base CreateOpts. +type CreateOptsExt struct { + VolumeCreateOptsBuilder + + // SchedulerHints provides a set of hints to the scheduler. + SchedulerHints CreateOptsBuilder +} + +// ToVolumeCreateMap adds the SchedulerHints option to the base volume creation options. +func (opts CreateOptsExt) ToVolumeCreateMap() (map[string]interface{}, error) { + base, err := opts.VolumeCreateOptsBuilder.ToVolumeCreateMap() + if err != nil { + return nil, err + } + + schedulerHints, err := opts.SchedulerHints.ToVolumeSchedulerHintsCreateMap() + if err != nil { + return nil, err + } + + if len(schedulerHints) == 0 { + return base, nil + } + + base["OS-SCH-HNT:scheduler_hints"] = schedulerHints + + return base, nil +} diff --git a/openstack/evs/extensions/schedulerhints/testing/doc.go b/openstack/evs/extensions/schedulerhints/testing/doc.go new file mode 100644 index 000000000..1915aef2f --- /dev/null +++ b/openstack/evs/extensions/schedulerhints/testing/doc.go @@ -0,0 +1,2 @@ +// schedulerhints unit tests +package testing diff --git a/openstack/evs/extensions/schedulerhints/testing/requests_test.go b/openstack/evs/extensions/schedulerhints/testing/requests_test.go new file mode 100644 index 000000000..2ba27c7ef --- /dev/null +++ b/openstack/evs/extensions/schedulerhints/testing/requests_test.go @@ -0,0 +1,58 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerhints" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreateOpts(t *testing.T) { + + base := volumes.CreateOpts{ + Size: 10, + Name: "testvolume", + } + schedulerHints := schedulerhints.SchedulerHints{ + DifferentHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + SameHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + LocalToInstance: "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", + AdditionalProperties: map[string]interface{}{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, + } + + ext := schedulerhints.CreateOptsExt{ + VolumeCreateOptsBuilder: base, + SchedulerHints: schedulerHints, + } + + expected := ` + { + "volume": { + "size": 10, + "name": "testvolume" + }, + "OS-SCH-HNT:scheduler_hints": { + "different_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "same_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "local_to_instance": "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", + "mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" + } + } + ` + actual, err := ext.ToVolumeCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} diff --git a/openstack/evs/extensions/schedulerstats/doc.go b/openstack/evs/extensions/schedulerstats/doc.go new file mode 100644 index 000000000..b0a2c8ff3 --- /dev/null +++ b/openstack/evs/extensions/schedulerstats/doc.go @@ -0,0 +1,23 @@ +/* +Package schedulerstats returns information about block storage pool capacity +and utilisation. Example: + + listOpts := schedulerstats.ListOpts{ + Detail: true, + } + + allPages, err := schedulerstats.List(client, listOpts).AllPages() + if err != nil { + panic(err) + } + + allStats, err := schedulerstats.ExtractStoragePools(allPages) + if err != nil { + panic(err) + } + + for _, stat := range allStats { + fmt.Printf("%+v\n", stat) + } +*/ +package schedulerstats diff --git a/openstack/evs/extensions/schedulerstats/requests.go b/openstack/evs/extensions/schedulerstats/requests.go new file mode 100644 index 000000000..7b374dcd8 --- /dev/null +++ b/openstack/evs/extensions/schedulerstats/requests.go @@ -0,0 +1,43 @@ +package schedulerstats + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToStoragePoolsListQuery() (string, error) +} + +// ListOpts controls the view of data returned (e.g globally or per project) +// via tenant_id and the verbosity via detail. +type ListOpts struct { + // ID of the tenant to look up storage pools for. + TenantID string `q:"tenant_id"` + + // Whether to list extended details. + Detail bool `q:"detail"` +} + +// ToStoragePoolsListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToStoragePoolsListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list storage pool information. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := storagePoolsListURL(client) + if opts != nil { + query, err := opts.ToStoragePoolsListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return StoragePoolPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/evs/extensions/schedulerstats/list.go b/openstack/evs/extensions/schedulerstats/results.go similarity index 50% rename from openstack/evs/extensions/schedulerstats/list.go rename to openstack/evs/extensions/schedulerstats/results.go index 0e661bb4c..672990713 100644 --- a/openstack/evs/extensions/schedulerstats/list.go +++ b/openstack/evs/extensions/schedulerstats/results.go @@ -3,39 +3,12 @@ package schedulerstats import ( "encoding/json" "math" + "strconv" - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" + "github.com/gophercloud/gophercloud/pagination" ) -type ListOpts struct { - // ID of the tenant to look up storage pools for. - TenantID string `q:"tenant_id"` - // Whether to list extended details. - Detail bool `q:"detail"` -} - -func List(client *golangsdk.ServiceClient, opts ListOpts) ([]StoragePool, error) { - query, err := golangsdk.BuildQueryString(opts) - if err != nil { - return nil, err - } - - raw, err := client.Get(client.ServiceURL("scheduler-stats", "get_pools")+query.String(), nil, nil) - if err != nil { - return nil, err - } - - var res []StoragePool - err = extract.IntoSlicePtr(raw.Body, &res, "pools") - return res, err -} - -type StoragePool struct { - Name string `json:"name"` - Capabilities Capabilities `json:"capabilities"` -} - +// Capabilities represents the information of an individual StoragePool. type Capabilities struct { // The following fields should be present in all storage drivers. DriverVersion string `json:"driver_version"` @@ -44,28 +17,39 @@ type Capabilities struct { TotalCapacityGB float64 `json:"-"` VendorName string `json:"vendor_name"` VolumeBackendName string `json:"volume_backend_name"` + // The following fields are optional and may have empty values depending // on the storage driver in use. ReservedPercentage int64 `json:"reserved_percentage"` LocationInfo string `json:"location_info"` QoSSupport bool `json:"QoS_support"` ProvisionedCapacityGB float64 `json:"provisioned_capacity_gb"` - MaxOverSubscriptionRatio string `json:"max_over_subscription_ratio"` + MaxOverSubscriptionRatio string `json:"-"` ThinProvisioningSupport bool `json:"thin_provisioning_support"` ThickProvisioningSupport bool `json:"thick_provisioning_support"` TotalVolumes int64 `json:"total_volumes"` FilterFunction string `json:"filter_function"` - GoodnessFuction string `json:"goodness_function"` - Mutliattach bool `json:"multiattach"` + GoodnessFunction string `json:"goodness_function"` + Multiattach bool `json:"multiattach"` SparseCopyVolume bool `json:"sparse_copy_volume"` + AllocatedCapacityGB float64 `json:"-"` +} + +// StoragePool represents an individual StoragePool retrieved from the +// schedulerstats API. +type StoragePool struct { + Name string `json:"name"` + Capabilities Capabilities `json:"capabilities"` } func (r *Capabilities) UnmarshalJSON(b []byte) error { type tmp Capabilities var s struct { tmp - FreeCapacityGB interface{} `json:"free_capacity_gb"` - TotalCapacityGB interface{} `json:"total_capacity_gb"` + AllocatedCapacityGB interface{} `json:"allocated_capacity_gb"` + FreeCapacityGB interface{} `json:"free_capacity_gb"` + MaxOverSubscriptionRatio interface{} `json:"max_over_subscription_ratio"` + TotalCapacityGB interface{} `json:"total_capacity_gb"` } err := json.Unmarshal(b, &s) if err != nil { @@ -77,11 +61,11 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { // value, "unknown", or "infinite" parseCapacity := func(capacity interface{}) float64 { if capacity != nil { - switch capacity := capacity.(type) { + switch capacity.(type) { case float64: - return capacity + return capacity.(float64) case string: - if capacity == "infinite" { + if capacity.(string) == "infinite" { return math.Inf(1) } } @@ -89,8 +73,40 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { return 0.0 } + r.AllocatedCapacityGB = parseCapacity(s.AllocatedCapacityGB) r.FreeCapacityGB = parseCapacity(s.FreeCapacityGB) r.TotalCapacityGB = parseCapacity(s.TotalCapacityGB) + if s.MaxOverSubscriptionRatio != nil { + switch t := s.MaxOverSubscriptionRatio.(type) { + case float64: + r.MaxOverSubscriptionRatio = strconv.FormatFloat(t, 'f', -1, 64) + case string: + r.MaxOverSubscriptionRatio = t + } + } + return nil } + +// StoragePoolPage is a single page of all List results. +type StoragePoolPage struct { + pagination.SinglePageBase +} + +// IsEmpty satisfies the IsEmpty method of the Page interface. It returns true +// if a List contains no results. +func (page StoragePoolPage) IsEmpty() (bool, error) { + va, err := ExtractStoragePools(page) + return len(va) == 0, err +} + +// ExtractStoragePools takes a List result and extracts the collection of +// StoragePools returned by the API. +func ExtractStoragePools(p pagination.Page) ([]StoragePool, error) { + var s struct { + StoragePools []StoragePool `json:"pools"` + } + err := (p.(StoragePoolPage)).ExtractInto(&s) + return s.StoragePools, err +} diff --git a/openstack/evs/extensions/schedulerstats/testing/fixtures.go b/openstack/evs/extensions/schedulerstats/testing/fixtures.go index 02a015c1d..3af0718a4 100644 --- a/openstack/evs/extensions/schedulerstats/testing/fixtures.go +++ b/openstack/evs/extensions/schedulerstats/testing/fixtures.go @@ -6,10 +6,9 @@ import ( "net/http" "testing" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/schedulerstats" - - "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" + "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" ) const StoragePoolsListBody = ` @@ -34,6 +33,7 @@ const StoragePoolsListBodyDetail = ` "filter_function": null, "free_capacity_gb": 64765, "goodness_function": null, + "max_over_subscription_ratio": "1.5", "multiattach": false, "reserved_percentage": 0, "storage_protocol": "ceph", @@ -50,6 +50,7 @@ const StoragePoolsListBodyDetail = ` "filter_function": null, "free_capacity_gb": "unknown", "goodness_function": null, + "max_over_subscription_ratio": 1.5, "multiattach": false, "reserved_percentage": 0, "storage_protocol": "ceph", @@ -68,24 +69,26 @@ var ( StoragePoolFake1 = schedulerstats.StoragePool{ Name: "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd", Capabilities: schedulerstats.Capabilities{ - DriverVersion: "1.2.0", - FreeCapacityGB: 64765, - StorageProtocol: "ceph", - TotalCapacityGB: 787947.93, - VendorName: "Open Source", - VolumeBackendName: "cinder.volumes.ssd", + DriverVersion: "1.2.0", + FreeCapacityGB: 64765, + MaxOverSubscriptionRatio: "1.5", + StorageProtocol: "ceph", + TotalCapacityGB: 787947.93, + VendorName: "Open Source", + VolumeBackendName: "cinder.volumes.ssd", }, } StoragePoolFake2 = schedulerstats.StoragePool{ Name: "rbd:cinder.volumes.hdd@cinder.volumes.hdd#cinder.volumes.hdd", Capabilities: schedulerstats.Capabilities{ - DriverVersion: "1.2.0", - FreeCapacityGB: 0.0, - StorageProtocol: "ceph", - TotalCapacityGB: math.Inf(1), - VendorName: "Open Source", - VolumeBackendName: "cinder.volumes.hdd", + DriverVersion: "1.2.0", + FreeCapacityGB: 0.0, + MaxOverSubscriptionRatio: "1.5", + StorageProtocol: "ceph", + TotalCapacityGB: math.Inf(1), + VendorName: "Open Source", + VolumeBackendName: "cinder.volumes.hdd", }, } ) @@ -97,11 +100,11 @@ func HandleStoragePoolsListSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") - _ = r.ParseForm() + r.ParseForm() if r.FormValue("detail") == "true" { - _, _ = fmt.Fprint(w, StoragePoolsListBodyDetail) + fmt.Fprintf(w, StoragePoolsListBodyDetail) } else { - _, _ = fmt.Fprint(w, StoragePoolsListBody) + fmt.Fprintf(w, StoragePoolsListBody) } }) } diff --git a/openstack/evs/extensions/schedulerstats/testing/requests_test.go b/openstack/evs/extensions/schedulerstats/testing/requests_test.go index ae6f64147..8a4ef5180 100644 --- a/openstack/evs/extensions/schedulerstats/testing/requests_test.go +++ b/openstack/evs/extensions/schedulerstats/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/schedulerstats" - - "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" + "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListStoragePoolsDetail(t *testing.T) { @@ -14,12 +14,25 @@ func TestListStoragePoolsDetail(t *testing.T) { defer testhelper.TeardownHTTP() HandleStoragePoolsListSuccessfully(t) - actual, err := schedulerstats.List(client.ServiceClient(), schedulerstats.ListOpts{Detail: true}) + pages := 0 + err := schedulerstats.List(client.ServiceClient(), schedulerstats.ListOpts{Detail: true}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := schedulerstats.ExtractStoragePools(page) + testhelper.AssertNoErr(t, err) + + if len(actual) != 2 { + t.Fatalf("Expected 2 backends, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, StoragePoolFake1, actual[0]) + testhelper.CheckDeepEquals(t, StoragePoolFake2, actual[1]) + + return true, nil + }) + testhelper.AssertNoErr(t, err) - if len(actual) != 2 { - t.Fatalf("Expected 2 backends, got %d", len(actual)) + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) } - testhelper.CheckDeepEquals(t, StoragePoolFake1, actual[0]) - testhelper.CheckDeepEquals(t, StoragePoolFake2, actual[1]) } diff --git a/openstack/evs/extensions/schedulerstats/urls.go b/openstack/evs/extensions/schedulerstats/urls.go new file mode 100644 index 000000000..c0ddb3695 --- /dev/null +++ b/openstack/evs/extensions/schedulerstats/urls.go @@ -0,0 +1,7 @@ +package schedulerstats + +import "github.com/gophercloud/gophercloud" + +func storagePoolsListURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("scheduler-stats", "get_pools") +} diff --git a/openstack/evs/extensions/services/doc.go b/openstack/evs/extensions/services/doc.go new file mode 100644 index 000000000..b3fba4cd6 --- /dev/null +++ b/openstack/evs/extensions/services/doc.go @@ -0,0 +1,22 @@ +/* +Package services returns information about the blockstorage services in the +OpenStack cloud. + +Example of Retrieving list of all services + + allPages, err := services.List(blockstorageClient, services.ListOpts{}).AllPages() + if err != nil { + panic(err) + } + + allServices, err := services.ExtractServices(allPages) + if err != nil { + panic(err) + } + + for _, service := range allServices { + fmt.Printf("%+v\n", service) + } +*/ + +package services diff --git a/openstack/evs/extensions/services/requests.go b/openstack/evs/extensions/services/requests.go new file mode 100644 index 000000000..0edcfc9d7 --- /dev/null +++ b/openstack/evs/extensions/services/requests.go @@ -0,0 +1,42 @@ +package services + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToServiceListQuery() (string, error) +} + +// ListOpts holds options for listing Services. +type ListOpts struct { + // Filter the service list result by binary name of the service. + Binary string `q:"binary"` + + // Filter the service list result by host name of the service. + Host string `q:"host"` +} + +// ToServiceListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToServiceListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list services. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToServiceListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ServicePage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/evs/extensions/services/list.go b/openstack/evs/extensions/services/results.go similarity index 57% rename from openstack/evs/extensions/services/list.go rename to openstack/evs/extensions/services/results.go index ef2755f21..49ad48ef6 100644 --- a/openstack/evs/extensions/services/list.go +++ b/openstack/evs/extensions/services/results.go @@ -4,72 +4,81 @@ import ( "encoding/json" "time" - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) -type ListOpts struct { - // Filter the service list result by binary name of the service. - Binary string `q:"binary"` - // Filter the service list result by host name of the service. - Host string `q:"host"` -} - -func List(client *golangsdk.ServiceClient, opts ListOpts) ([]Service, error) { - query, err := golangsdk.BuildQueryString(opts) - if err != nil { - return nil, err - } - - raw, err := client.Get(client.ServiceURL("os-services")+query.String(), nil, nil) - if err != nil { - return nil, err - } - - var res []Service - err = extract.IntoSlicePtr(raw.Body, &res, "services") - return res, err -} - +// Service represents a Blockstorage service in the OpenStack cloud. type Service struct { // The binary name of the service. Binary string `json:"binary"` + // The reason for disabling a service. DisabledReason string `json:"disabled_reason"` + // The name of the host. Host string `json:"host"` + // The state of the service. One of up or down. State string `json:"state"` + // The status of the service. One of available or unavailable. Status string `json:"status"` + // The date and time stamp when the extension was last updated. UpdatedAt time.Time `json:"-"` + // The availability zone name. Zone string `json:"zone"` + // The following fields are optional + // The host is frozen or not. Only in cinder-volume service. Frozen bool `json:"frozen"` + // The cluster name. Only in cinder-volume service. Cluster string `json:"cluster"` + // The volume service replication status. Only in cinder-volume service. ReplicationStatus string `json:"replication_status"` + // The ID of active storage backend. Only in cinder-volume service. ActiveBackendID string `json:"active_backend_id"` } +// UnmarshalJSON to override default func (r *Service) UnmarshalJSON(b []byte) error { type tmp Service - var res struct { + var s struct { tmp - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } - err := json.Unmarshal(b, &res) + err := json.Unmarshal(b, &s) if err != nil { return err } - *r = Service(res.tmp) + *r = Service(s.tmp) - r.UpdatedAt = time.Time(res.UpdatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) return nil } + +// ServicePage represents a single page of all Services from a List request. +type ServicePage struct { + pagination.SinglePageBase +} + +// IsEmpty determines whether or not a page of Services contains any results. +func (page ServicePage) IsEmpty() (bool, error) { + services, err := ExtractServices(page) + return len(services) == 0, err +} + +func ExtractServices(r pagination.Page) ([]Service, error) { + var s struct { + Service []Service `json:"services"` + } + err := (r.(ServicePage)).ExtractInto(&s) + return s.Service, err +} diff --git a/openstack/evs/extensions/services/testing/fixtures.go b/openstack/evs/extensions/services/testing/fixtures.go index c98b11b0b..9d14723c1 100644 --- a/openstack/evs/extensions/services/testing/fixtures.go +++ b/openstack/evs/extensions/services/testing/fixtures.go @@ -6,10 +6,9 @@ import ( "testing" "time" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/services" - - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" ) // ServiceListBody is sample response to the List call @@ -93,6 +92,6 @@ func HandleListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - _, _ = fmt.Fprint(w, ServiceListBody) + fmt.Fprintf(w, ServiceListBody) }) } diff --git a/openstack/evs/extensions/services/testing/requests_test.go b/openstack/evs/extensions/services/testing/requests_test.go index 7d06b67d1..4178c2369 100644 --- a/openstack/evs/extensions/services/testing/requests_test.go +++ b/openstack/evs/extensions/services/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/services" - - "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" + "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListServices(t *testing.T) { @@ -14,13 +14,28 @@ func TestListServices(t *testing.T) { defer testhelper.TeardownHTTP() HandleListSuccessfully(t) - actual, err := services.List(client.ServiceClient(), services.ListOpts{}) + pages := 0 + err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := services.ExtractServices(page) + if err != nil { + return false, err + } + + if len(actual) != 3 { + t.Fatalf("Expected 3 services, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) + testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) + testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) + + return true, nil + }) + testhelper.AssertNoErr(t, err) - if len(actual) != 3 { - t.Fatalf("Expected 3 services, got %d", len(actual)) + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) } - testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) - testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) - testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) } diff --git a/openstack/evs/extensions/services/urls.go b/openstack/evs/extensions/services/urls.go new file mode 100644 index 000000000..61d794007 --- /dev/null +++ b/openstack/evs/extensions/services/urls.go @@ -0,0 +1,7 @@ +package services + +import "github.com/gophercloud/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-services") +} diff --git a/openstack/evs/extensions/volumeactions/attach.go b/openstack/evs/extensions/volumeactions/attach.go deleted file mode 100644 index 0d176c4e3..000000000 --- a/openstack/evs/extensions/volumeactions/attach.go +++ /dev/null @@ -1,31 +0,0 @@ -package volumeactions - -import "github.com/opentelekomcloud/gophertelekomcloud" - -type AttachMode string - -const ( - ReadOnly AttachMode = "ro" - ReadWrite AttachMode = "rw" -) - -type AttachOpts struct { - // The mountpoint of this volume. - MountPoint string `json:"mountpoint,omitempty"` - // The nova instance ID, can't set simultaneously with HostName. - InstanceUUID string `json:"instance_uuid,omitempty"` - // The hostname of baremetal host, can't set simultaneously with InstanceUUID. - HostName string `json:"host_name,omitempty"` - // Mount mode of this volume. - Mode AttachMode `json:"mode,omitempty"` -} - -func Attach(client *golangsdk.ServiceClient, id string, opts AttachOpts) (err error) { - b, err := golangsdk.BuildRequestBody(opts, "os-attach") - if err != nil { - return - } - - _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, nil) - return -} diff --git a/openstack/evs/extensions/volumeactions/begin_detaching.go b/openstack/evs/extensions/volumeactions/begin_detaching.go deleted file mode 100644 index 0835090ad..000000000 --- a/openstack/evs/extensions/volumeactions/begin_detaching.go +++ /dev/null @@ -1,9 +0,0 @@ -package volumeactions - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func BeginDetaching(client *golangsdk.ServiceClient, id string) (err error) { - b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} - _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, nil) - return -} diff --git a/openstack/evs/extensions/volumeactions/detach.go b/openstack/evs/extensions/volumeactions/detach.go deleted file mode 100644 index cd54d2780..000000000 --- a/openstack/evs/extensions/volumeactions/detach.go +++ /dev/null @@ -1,18 +0,0 @@ -package volumeactions - -import "github.com/opentelekomcloud/gophertelekomcloud" - -type DetachOpts struct { - // AttachmentID is the ID of the attachment between a volume and instance. - AttachmentID string `json:"attachment_id,omitempty"` -} - -func Detach(client *golangsdk.ServiceClient, id string, opts DetachOpts) (err error) { - b, err := golangsdk.BuildRequestBody(opts, "os-detach") - if err != nil { - return - } - - _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, nil) - return -} diff --git a/openstack/evs/extensions/volumeactions/doc.go b/openstack/evs/extensions/volumeactions/doc.go new file mode 100644 index 000000000..34db834f7 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/doc.go @@ -0,0 +1,108 @@ +/* +Package volumeactions provides information and interaction with volumes in the +OpenStack Block Storage service. A volume is a detachable block storage +device, akin to a USB hard drive. + +Example of Attaching a Volume to an Instance + + attachOpts := volumeactions.AttachOpts{ + MountPoint: "/mnt", + Mode: "rw", + InstanceUUID: server.ID, + } + + err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr() + if err != nil { + panic(err) + } + + detachOpts := volumeactions.DetachOpts{ + AttachmentID: volume.Attachments[0].AttachmentID, + } + + err = volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Creating an Image from a Volume + + uploadImageOpts := volumeactions.UploadImageOpts{ + ImageName: "my_vol", + Force: true, + } + + volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", volumeImage) + +Example of Extending a Volume's Size + + extendOpts := volumeactions.ExtendSizeOpts{ + NewSize: 100, + } + + err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Initializing a Volume Connection + + connectOpts := &volumeactions.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + + connectionInfo, err := volumeactions.InitializeConnection(client, volume.ID, connectOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", connectionInfo["data"]) + + terminateOpts := &volumeactions.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + + err = volumeactions.TerminateConnection(client, volume.ID, terminateOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Setting a Volume's Bootable status + + options := volumeactions.BootableOpts{ + Bootable: true, + } + + err := volumeactions.SetBootable(client, volume.ID, options).ExtractErr() + if err != nil { + panic(err) + } + +Example of Changing Type of a Volume + + changeTypeOpts := volumeactions.ChangeTypeOpts{ + NewType: "ssd", + MigrationPolicy: volumeactions.MigrationPolicyOnDemand, + } + + err = volumeactions.ChangeType(client, volumeID, changeTypeOpts).ExtractErr() + if err != nil { + panic(err) + } +*/ +package volumeactions diff --git a/openstack/evs/extensions/volumeactions/extend_size.go b/openstack/evs/extensions/volumeactions/extend_size.go deleted file mode 100644 index 28a24f75b..000000000 --- a/openstack/evs/extensions/volumeactions/extend_size.go +++ /dev/null @@ -1,18 +0,0 @@ -package volumeactions - -import "github.com/opentelekomcloud/gophertelekomcloud" - -type ExtendSizeOpts struct { - // NewSize is the new size of the volume, in GB. - NewSize int `json:"new_size" required:"true"` -} - -func ExtendSize(client *golangsdk.ServiceClient, id string, opts ExtendSizeOpts) (err error) { - b, err := golangsdk.BuildRequestBody(opts, "os-extend") - if err != nil { - return - } - - _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, nil) - return -} diff --git a/openstack/evs/extensions/volumeactions/force_delete.go b/openstack/evs/extensions/volumeactions/force_delete.go deleted file mode 100644 index 7082b4ff9..000000000 --- a/openstack/evs/extensions/volumeactions/force_delete.go +++ /dev/null @@ -1,10 +0,0 @@ -package volumeactions - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -func ForceDelete(client *golangsdk.ServiceClient, id string) (err error) { - _, err = client.Post(client.ServiceURL("volumes", id, "action"), map[string]interface{}{"os-force_delete": ""}, nil, nil) - return -} diff --git a/openstack/evs/extensions/volumeactions/initialize_connection.go b/openstack/evs/extensions/volumeactions/initialize_connection.go deleted file mode 100644 index 91ae63629..000000000 --- a/openstack/evs/extensions/volumeactions/initialize_connection.go +++ /dev/null @@ -1,38 +0,0 @@ -package volumeactions - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" -) - -type InitializeConnectionOpts struct { - IP string `json:"ip,omitempty"` - Host string `json:"host,omitempty"` - Initiator string `json:"initiator,omitempty"` - Wwpns []string `json:"wwpns,omitempty"` - Wwnns string `json:"wwnns,omitempty"` - Multipath *bool `json:"multipath,omitempty"` - Platform string `json:"platform,omitempty"` - OSType string `json:"os_type,omitempty"` -} - -func InitializeConnection(client *golangsdk.ServiceClient, id string, opts InitializeConnectionOpts) (map[string]interface{}, error) { - b, err := golangsdk.BuildRequestBody(opts, "connector") - b = map[string]interface{}{"os-initialize_connection": b} - if err != nil { - return nil, err - } - - raw, err := client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - if err != nil { - return nil, err - } - - var res struct { - ConnectionInfo map[string]interface{} `json:"connection_info"` - } - err = extract.Into(raw.Body, &res) - return res.ConnectionInfo, err -} diff --git a/openstack/evs/extensions/volumeactions/requests.go b/openstack/evs/extensions/volumeactions/requests.go new file mode 100644 index 000000000..09dfb9ed2 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/requests.go @@ -0,0 +1,420 @@ +package volumeactions + +import ( + "github.com/gophercloud/gophercloud" +) + +// AttachOptsBuilder allows extensions to add additional parameters to the +// Attach request. +type AttachOptsBuilder interface { + ToVolumeAttachMap() (map[string]interface{}, error) +} + +// AttachMode describes the attachment mode for volumes. +type AttachMode string + +// These constants determine how a volume is attached. +const ( + ReadOnly AttachMode = "ro" + ReadWrite AttachMode = "rw" +) + +// AttachOpts contains options for attaching a Volume. +type AttachOpts struct { + // The mountpoint of this volume. + MountPoint string `json:"mountpoint,omitempty"` + + // The nova instance ID, can't set simultaneously with HostName. + InstanceUUID string `json:"instance_uuid,omitempty"` + + // The hostname of baremetal host, can't set simultaneously with InstanceUUID. + HostName string `json:"host_name,omitempty"` + + // Mount mode of this volume. + Mode AttachMode `json:"mode,omitempty"` +} + +// ToVolumeAttachMap assembles a request body based on the contents of a +// AttachOpts. +func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-attach") +} + +// Attach will attach a volume based on the values in AttachOpts. +func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) { + b, err := opts.ToVolumeAttachMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// BeginDetaching will mark the volume as detaching. +func BeginDetaching(client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { + b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DetachOptsBuilder allows extensions to add additional parameters to the +// Detach request. +type DetachOptsBuilder interface { + ToVolumeDetachMap() (map[string]interface{}, error) +} + +// DetachOpts contains options for detaching a Volume. +type DetachOpts struct { + // AttachmentID is the ID of the attachment between a volume and instance. + AttachmentID string `json:"attachment_id,omitempty"` +} + +// ToVolumeDetachMap assembles a request body based on the contents of a +// DetachOpts. +func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-detach") +} + +// Detach will detach a volume based on volume ID. +func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { + b, err := opts.ToVolumeDetachMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Reserve will reserve a volume based on volume ID. +func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) { + b := map[string]interface{}{"os-reserve": make(map[string]interface{})} + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Unreserve will unreserve a volume based on volume ID. +func Unreserve(client *gophercloud.ServiceClient, id string) (r UnreserveResult) { + b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// InitializeConnectionOptsBuilder allows extensions to add additional parameters to the +// InitializeConnection request. +type InitializeConnectionOptsBuilder interface { + ToVolumeInitializeConnectionMap() (map[string]interface{}, error) +} + +// InitializeConnectionOpts hosts options for InitializeConnection. +// The fields are specific to the storage driver in use and the destination +// attachment. +type InitializeConnectionOpts struct { + IP string `json:"ip,omitempty"` + Host string `json:"host,omitempty"` + Initiator string `json:"initiator,omitempty"` + Wwpns []string `json:"wwpns,omitempty"` + Wwnns string `json:"wwnns,omitempty"` + Multipath *bool `json:"multipath,omitempty"` + Platform string `json:"platform,omitempty"` + OSType string `json:"os_type,omitempty"` +} + +// ToVolumeInitializeConnectionMap assembles a request body based on the contents of a +// InitializeConnectionOpts. +func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "connector") + return map[string]interface{}{"os-initialize_connection": b}, err +} + +// InitializeConnection initializes an iSCSI connection by volume ID. +func InitializeConnection(client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { + b, err := opts.ToVolumeInitializeConnectionMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// TerminateConnectionOptsBuilder allows extensions to add additional parameters to the +// TerminateConnection request. +type TerminateConnectionOptsBuilder interface { + ToVolumeTerminateConnectionMap() (map[string]interface{}, error) +} + +// TerminateConnectionOpts hosts options for TerminateConnection. +type TerminateConnectionOpts struct { + IP string `json:"ip,omitempty"` + Host string `json:"host,omitempty"` + Initiator string `json:"initiator,omitempty"` + Wwpns []string `json:"wwpns,omitempty"` + Wwnns string `json:"wwnns,omitempty"` + Multipath *bool `json:"multipath,omitempty"` + Platform string `json:"platform,omitempty"` + OSType string `json:"os_type,omitempty"` +} + +// ToVolumeTerminateConnectionMap assembles a request body based on the contents of a +// TerminateConnectionOpts. +func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "connector") + return map[string]interface{}{"os-terminate_connection": b}, err +} + +// TerminateConnection terminates an iSCSI connection by volume ID. +func TerminateConnection(client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { + b, err := opts.ToVolumeTerminateConnectionMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ExtendSizeOptsBuilder allows extensions to add additional parameters to the +// ExtendSize request. +type ExtendSizeOptsBuilder interface { + ToVolumeExtendSizeMap() (map[string]interface{}, error) +} + +// ExtendSizeOpts contains options for extending the size of an existing Volume. +// This object is passed to the volumes.ExtendSize function. +type ExtendSizeOpts struct { + // NewSize is the new size of the volume, in GB. + NewSize int `json:"new_size" required:"true"` +} + +// ToVolumeExtendSizeMap assembles a request body based on the contents of an +// ExtendSizeOpts. +func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-extend") +} + +// ExtendSize will extend the size of the volume based on the provided information. +// This operation does not return a response body. +func ExtendSize(client *gophercloud.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) { + b, err := opts.ToVolumeExtendSizeMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UploadImageOptsBuilder allows extensions to add additional parameters to the +// UploadImage request. +type UploadImageOptsBuilder interface { + ToVolumeUploadImageMap() (map[string]interface{}, error) +} + +// UploadImageOpts contains options for uploading a Volume to image storage. +type UploadImageOpts struct { + // Container format, may be bare, ofv, ova, etc. + ContainerFormat string `json:"container_format,omitempty"` + + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + DiskFormat string `json:"disk_format,omitempty"` + + // The name of image that will be stored in glance. + ImageName string `json:"image_name,omitempty"` + + // Force image creation, usable if volume attached to instance. + Force bool `json:"force,omitempty"` + + // Visibility defines who can see/use the image. + // supported since 3.1 microversion + Visibility string `json:"visibility,omitempty"` + + // whether the image is not deletable. + // supported since 3.1 microversion + Protected bool `json:"protected,omitempty"` +} + +// ToVolumeUploadImageMap assembles a request body based on the contents of a +// UploadImageOpts. +func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-volume_upload_image") +} + +// UploadImage will upload an image based on the values in UploadImageOptsBuilder. +func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { + b, err := opts.ToVolumeUploadImageMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ForceDelete will delete the volume regardless of state. +func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + resp, err := client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ImageMetadataOptsBuilder allows extensions to add additional parameters to the +// ImageMetadataRequest request. +type ImageMetadataOptsBuilder interface { + ToImageMetadataMap() (map[string]interface{}, error) +} + +// ImageMetadataOpts contains options for setting image metadata to a volume. +type ImageMetadataOpts struct { + // The image metadata to add to the volume as a set of metadata key and value pairs. + Metadata map[string]string `json:"metadata"` +} + +// ToImageMetadataMap assembles a request body based on the contents of a +// ImageMetadataOpts. +func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-set_image_metadata") +} + +// SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder. +func SetImageMetadata(client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { + b, err := opts.ToImageMetadataMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// BootableOpts contains options for setting bootable status to a volume. +type BootableOpts struct { + // Enables or disables the bootable attribute. You can boot an instance from a bootable volume. + Bootable bool `json:"bootable"` +} + +// ToBootableMap assembles a request body based on the contents of a +// BootableOpts. +func (opts BootableOpts) ToBootableMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-set_bootable") +} + +// SetBootable will set bootable status on a volume based on the values in BootableOpts +func SetBootable(client *gophercloud.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { + b, err := opts.ToBootableMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// MigrationPolicy type represents a migration_policy when changing types. +type MigrationPolicy string + +// Supported attributes for MigrationPolicy attribute for changeType operations. +const ( + MigrationPolicyNever MigrationPolicy = "never" + MigrationPolicyOnDemand MigrationPolicy = "on-demand" +) + +// ChangeTypeOptsBuilder allows extensions to add additional parameters to the +// ChangeType request. +type ChangeTypeOptsBuilder interface { + ToVolumeChangeTypeMap() (map[string]interface{}, error) +} + +// ChangeTypeOpts contains options for changing the type of an existing Volume. +// This object is passed to the volumes.ChangeType function. +type ChangeTypeOpts struct { + // NewType is the name of the new volume type of the volume. + NewType string `json:"new_type" required:"true"` + + // MigrationPolicy specifies if the volume should be migrated when it is + // re-typed. Possible values are "on-demand" or "never". If not specified, + // the default is "never". + MigrationPolicy MigrationPolicy `json:"migration_policy,omitempty"` +} + +// ToVolumeChangeTypeMap assembles a request body based on the contents of an +// ChangeTypeOpts. +func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-retype") +} + +// ChangeType will change the volume type of the volume based on the provided information. +// This operation does not return a response body. +func ChangeType(client *gophercloud.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) { + b, err := opts.ToVolumeChangeTypeMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ReImageOpts contains options for Re-image a volume. +type ReImageOpts struct { + // New image id + ImageID string `json:"image_id"` + // set true to re-image volumes in reserved state + ReImageReserved bool `json:"reimage_reserved"` +} + +// ToReImageMap assembles a request body based on the contents of a ReImageOpts. +func (opts ReImageOpts) ToReImageMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-reimage") +} + +// ReImage will re-image a volume based on the values in ReImageOpts +func ReImage(client *gophercloud.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) { + b, err := opts.ToReImageMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/extensions/volumeactions/reserve.go b/openstack/evs/extensions/volumeactions/reserve.go deleted file mode 100644 index 19bb18293..000000000 --- a/openstack/evs/extensions/volumeactions/reserve.go +++ /dev/null @@ -1,11 +0,0 @@ -package volumeactions - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func Reserve(client *golangsdk.ServiceClient, id string) (err error) { - b := map[string]interface{}{"os-reserve": make(map[string]interface{})} - _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - return -} diff --git a/openstack/evs/extensions/volumeactions/results.go b/openstack/evs/extensions/volumeactions/results.go new file mode 100644 index 000000000..95b5bac1c --- /dev/null +++ b/openstack/evs/extensions/volumeactions/results.go @@ -0,0 +1,221 @@ +package volumeactions + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" +) + +// AttachResult contains the response body and error from an Attach request. +type AttachResult struct { + gophercloud.ErrResult +} + +// BeginDetachingResult contains the response body and error from a BeginDetach +// request. +type BeginDetachingResult struct { + gophercloud.ErrResult +} + +// DetachResult contains the response body and error from a Detach request. +type DetachResult struct { + gophercloud.ErrResult +} + +// UploadImageResult contains the response body and error from an UploadImage +// request. +type UploadImageResult struct { + gophercloud.Result +} + +// SetImageMetadataResult contains the response body and error from an SetImageMetadata +// request. +type SetImageMetadataResult struct { + gophercloud.ErrResult +} + +// SetBootableResult contains the response body and error from a SetBootable +// request. +type SetBootableResult struct { + gophercloud.ErrResult +} + +// ReserveResult contains the response body and error from a Reserve request. +type ReserveResult struct { + gophercloud.ErrResult +} + +// UnreserveResult contains the response body and error from an Unreserve +// request. +type UnreserveResult struct { + gophercloud.ErrResult +} + +// TerminateConnectionResult contains the response body and error from a +// TerminateConnection request. +type TerminateConnectionResult struct { + gophercloud.ErrResult +} + +// InitializeConnectionResult contains the response body and error from an +// InitializeConnection request. +type InitializeConnectionResult struct { + gophercloud.Result +} + +// ExtendSizeResult contains the response body and error from an ExtendSize request. +type ExtendSizeResult struct { + gophercloud.ErrResult +} + +// Extract will get the connection information out of the +// InitializeConnectionResult object. +// +// This will be a generic map[string]interface{} and the results will be +// dependent on the type of connection made. +func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) { + var s struct { + ConnectionInfo map[string]interface{} `json:"connection_info"` + } + err := r.ExtractInto(&s) + return s.ConnectionInfo, err +} + +// ImageVolumeType contains volume type information obtained from UploadImage +// action. +type ImageVolumeType struct { + // The ID of a volume type. + ID string `json:"id"` + + // Human-readable display name for the volume type. + Name string `json:"name"` + + // Human-readable description for the volume type. + Description string `json:"display_description"` + + // Flag for public access. + IsPublic bool `json:"is_public"` + + // Extra specifications for volume type. + ExtraSpecs map[string]interface{} `json:"extra_specs"` + + // ID of quality of service specs. + QosSpecsID string `json:"qos_specs_id"` + + // Flag for deletion status of volume type. + Deleted bool `json:"deleted"` + + // The date when volume type was deleted. + DeletedAt time.Time `json:"-"` + + // The date when volume type was created. + CreatedAt time.Time `json:"-"` + + // The date when this volume was last updated. + UpdatedAt time.Time `json:"-"` +} + +func (r *ImageVolumeType) UnmarshalJSON(b []byte) error { + type tmp ImageVolumeType + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = ImageVolumeType(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.DeletedAt = time.Time(s.DeletedAt) + + return err +} + +// VolumeImage contains information about volume uploaded to an image service. +type VolumeImage struct { + // The ID of a volume an image is created from. + VolumeID string `json:"id"` + + // Container format, may be bare, ofv, ova, etc. + ContainerFormat string `json:"container_format"` + + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + DiskFormat string `json:"disk_format"` + + // Human-readable description for the volume. + Description string `json:"display_description"` + + // The ID of the created image. + ImageID string `json:"image_id"` + + // Human-readable display name for the image. + ImageName string `json:"image_name"` + + // Size of the volume in GB. + Size int `json:"size"` + + // Current status of the volume. + Status string `json:"status"` + + // Visibility defines who can see/use the image. + // supported since 3.1 microversion + Visibility string `json:"visibility"` + + // whether the image is not deletable. + // supported since 3.1 microversion + Protected bool `json:"protected"` + + // The date when this volume was last updated. + UpdatedAt time.Time `json:"-"` + + // Volume type object of used volume. + VolumeType ImageVolumeType `json:"volume_type"` +} + +func (r *VolumeImage) UnmarshalJSON(b []byte) error { + type tmp VolumeImage + var s struct { + tmp + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = VolumeImage(s.tmp) + + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} + +// Extract will get an object with info about the uploaded image out of the +// UploadImageResult object. +func (r UploadImageResult) Extract() (VolumeImage, error) { + var s struct { + VolumeImage VolumeImage `json:"os-volume_upload_image"` + } + err := r.ExtractInto(&s) + return s.VolumeImage, err +} + +// ForceDeleteResult contains the response body and error from a ForceDelete request. +type ForceDeleteResult struct { + gophercloud.ErrResult +} + +// ChangeTypeResult contains the response body and error from an ChangeType request. +type ChangeTypeResult struct { + gophercloud.ErrResult +} + +// ReImageResult contains the response body and error from a ReImage request. +type ReImageResult struct { + gophercloud.ErrResult +} diff --git a/openstack/evs/extensions/volumeactions/terminate_connection.go b/openstack/evs/extensions/volumeactions/terminate_connection.go deleted file mode 100644 index 781943259..000000000 --- a/openstack/evs/extensions/volumeactions/terminate_connection.go +++ /dev/null @@ -1,30 +0,0 @@ -package volumeactions - -import "github.com/opentelekomcloud/gophertelekomcloud" - -type TerminateConnectionOpts struct { - IP string `json:"ip,omitempty"` - Host string `json:"host,omitempty"` - Initiator string `json:"initiator,omitempty"` - Wwpns []string `json:"wwpns,omitempty"` - Wwnns string `json:"wwnns,omitempty"` - Multipath *bool `json:"multipath,omitempty"` - Platform string `json:"platform,omitempty"` - OSType string `json:"os_type,omitempty"` -} - -func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { - b, err := golangsdk.BuildRequestBody(opts, "connector") - return map[string]interface{}{"os-terminate_connection": b}, err -} - -func TerminateConnection(client *golangsdk.ServiceClient, id string, opts TerminateConnectionOpts) (err error) { - b, err := golangsdk.BuildRequestBody(opts, "connector") - b = map[string]interface{}{"os-terminate_connection": b} - if err != nil { - return - } - - _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, nil) - return -} diff --git a/openstack/evs/extensions/volumeactions/testing/doc.go b/openstack/evs/extensions/volumeactions/testing/doc.go new file mode 100644 index 000000000..336406df1 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/testing/doc.go @@ -0,0 +1,2 @@ +// volumeactions unit tests +package testing diff --git a/openstack/evs/extensions/volumeactions/testing/fixtures.go b/openstack/evs/extensions/volumeactions/testing/fixtures.go index fed8bc879..378a120bc 100644 --- a/openstack/evs/extensions/volumeactions/testing/fixtures.go +++ b/openstack/evs/extensions/volumeactions/testing/fixtures.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - fake "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockAttachResponse(t *testing.T) { @@ -30,7 +30,7 @@ func MockAttachResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - _, _ = fmt.Fprint(w, `{}`) + fmt.Fprintf(w, `{}`) }) } @@ -50,7 +50,7 @@ func MockBeginDetachingResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - _, _ = fmt.Fprint(w, `{}`) + fmt.Fprintf(w, `{}`) }) } @@ -70,7 +70,7 @@ func MockDetachResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - _, _ = fmt.Fprint(w, `{}`) + fmt.Fprintf(w, `{}`) }) } @@ -95,7 +95,7 @@ func MockUploadImageResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - _, _ = fmt.Fprint(w, ` + fmt.Fprintf(w, ` { "os-volume_upload_image": { "container_format": "bare", @@ -142,7 +142,7 @@ func MockReserveResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - _, _ = fmt.Fprint(w, `{}`) + fmt.Fprintf(w, `{}`) }) } @@ -162,7 +162,7 @@ func MockUnreserveResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - _, _ = fmt.Fprint(w, `{}`) + fmt.Fprintf(w, `{}`) }) } @@ -193,7 +193,7 @@ func MockInitializeConnectionResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - _, _ = fmt.Fprint(w, `{ + fmt.Fprintf(w, `{ "connection_info": { "data": { "target_portals": [ @@ -250,7 +250,7 @@ func MockTerminateConnectionResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - _, _ = fmt.Fprint(w, `{}`) + fmt.Fprintf(w, `{}`) }) } @@ -273,7 +273,7 @@ func MockExtendSizeResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - _, _ = fmt.Fprint(w, `{}`) + fmt.Fprintf(w, `{}`) }) } @@ -281,11 +281,92 @@ func MockForceDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestBody(t, r, `{"os-force_delete":""}`) + w.WriteHeader(http.StatusAccepted) + }) +} + +func MockSetImageMetadataResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", 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, ` +{ + "os-set_image_metadata": { + "metadata": { + "label": "test" + } + } +} + `) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockSetBootableResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", 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, ` +{ + "os-set_bootable": { + "bootable": true + } +} + `) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Length", "0") + w.WriteHeader(http.StatusOK) + }) +} + +func MockReImageResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", 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, ` { - "os-force_delete": "" + "os-reimage": { + "image_id": "71543ced-a8af-45b6-a5c4-a46282108a90", + "reimage_reserved": false + } } `) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Length", "0") w.WriteHeader(http.StatusAccepted) }) } + +func MockChangeTypeResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", + 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, ` +{ + "os-retype": + { + "new_type": "ssd", + "migration_policy": "on-demand" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} diff --git a/openstack/evs/extensions/volumeactions/testing/requests_test.go b/openstack/evs/extensions/volumeactions/testing/requests_test.go index 95bb9d9cb..2191a8a78 100644 --- a/openstack/evs/extensions/volumeactions/testing/requests_test.go +++ b/openstack/evs/extensions/volumeactions/testing/requests_test.go @@ -4,11 +4,10 @@ import ( "testing" "time" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/volumeactions" - - "github.com/opentelekomcloud/gophertelekomcloud" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" ) func TestAttach(t *testing.T) { @@ -17,12 +16,12 @@ func TestAttach(t *testing.T) { MockAttachResponse(t) - options := volumeactions.AttachOpts{ + options := &volumeactions.AttachOpts{ MountPoint: "/mnt", Mode: "rw", InstanceUUID: "50902f4f-a974-46a0-85e9-7efc5e22dfdd", } - err := volumeactions.Attach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options) + err := volumeactions.Attach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } @@ -32,7 +31,7 @@ func TestBeginDetaching(t *testing.T) { MockBeginDetachingResponse(t) - err := volumeactions.BeginDetaching(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c") + err := volumeactions.BeginDetaching(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() th.AssertNoErr(t, err) } @@ -42,7 +41,7 @@ func TestDetach(t *testing.T) { MockDetachResponse(t) - err := volumeactions.Detach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", volumeactions.DetachOpts{}) + err := volumeactions.Detach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", &volumeactions.DetachOpts{}).ExtractErr() th.AssertNoErr(t, err) } @@ -50,14 +49,14 @@ func TestUploadImage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUploadImageResponse(t) - options := volumeactions.UploadImageOpts{ + options := &volumeactions.UploadImageOpts{ ContainerFormat: "bare", DiskFormat: "raw", ImageName: "test", Force: true, } - actual, err := volumeactions.UploadImage(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options) + actual, err := volumeactions.UploadImage(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() th.AssertNoErr(t, err) expected := volumeactions.VolumeImage{ @@ -83,7 +82,7 @@ func TestUploadImage(t *testing.T) { UpdatedAt: time.Date(2016, 5, 4, 9, 15, 33, 0, time.UTC), }, } - th.AssertDeepEquals(t, &expected, actual) + th.AssertDeepEquals(t, expected, actual) } func TestReserve(t *testing.T) { @@ -92,7 +91,7 @@ func TestReserve(t *testing.T) { MockReserveResponse(t) - err := volumeactions.Reserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c") + err := volumeactions.Reserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() th.AssertNoErr(t, err) } @@ -102,7 +101,7 @@ func TestUnreserve(t *testing.T) { MockUnreserveResponse(t) - err := volumeactions.Unreserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c") + err := volumeactions.Unreserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() th.AssertNoErr(t, err) } @@ -112,15 +111,15 @@ func TestInitializeConnection(t *testing.T) { MockInitializeConnectionResponse(t) - options := volumeactions.InitializeConnectionOpts{ + options := &volumeactions.InitializeConnectionOpts{ IP: "127.0.0.1", Host: "stack", Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: golangsdk.Disabled, + Multipath: gophercloud.Disabled, Platform: "x86_64", OSType: "linux2", } - _, err := volumeactions.InitializeConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options) + _, err := volumeactions.InitializeConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() th.AssertNoErr(t, err) } @@ -130,15 +129,15 @@ func TestTerminateConnection(t *testing.T) { MockTerminateConnectionResponse(t) - options := volumeactions.TerminateConnectionOpts{ + options := &volumeactions.TerminateConnectionOpts{ IP: "127.0.0.1", Host: "stack", Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: golangsdk.Enabled, + Multipath: gophercloud.Enabled, Platform: "x86_64", OSType: "linux2", } - err := volumeactions.TerminateConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options) + err := volumeactions.TerminateConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } @@ -148,11 +147,11 @@ func TestExtendSize(t *testing.T) { MockExtendSizeResponse(t) - options := volumeactions.ExtendSizeOpts{ + options := &volumeactions.ExtendSizeOpts{ NewSize: 3, } - err := volumeactions.ExtendSize(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options) + err := volumeactions.ExtendSize(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } @@ -163,5 +162,65 @@ func TestForceDelete(t *testing.T) { MockForceDeleteResponse(t) res := volumeactions.ForceDelete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res) + th.AssertNoErr(t, res.Err) +} + +func TestSetImageMetadata(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockSetImageMetadataResponse(t) + + options := &volumeactions.ImageMetadataOpts{ + Metadata: map[string]string{ + "label": "test", + }, + } + + err := volumeactions.SetImageMetadata(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestSetBootable(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockSetBootableResponse(t) + + options := volumeactions.BootableOpts{ + Bootable: true, + } + + err := volumeactions.SetBootable(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestReImage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockReImageResponse(t) + + options := volumeactions.ReImageOpts{ + ImageID: "71543ced-a8af-45b6-a5c4-a46282108a90", + ReImageReserved: false, + } + + err := volumeactions.ReImage(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestChangeType(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockChangeTypeResponse(t) + + options := &volumeactions.ChangeTypeOpts{ + NewType: "ssd", + MigrationPolicy: "on-demand", + } + + err := volumeactions.ChangeType(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) } diff --git a/openstack/evs/extensions/volumeactions/unreserve.go b/openstack/evs/extensions/volumeactions/unreserve.go deleted file mode 100644 index 5eaf9ec96..000000000 --- a/openstack/evs/extensions/volumeactions/unreserve.go +++ /dev/null @@ -1,11 +0,0 @@ -package volumeactions - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func Unreserve(client *golangsdk.ServiceClient, id string) (err error) { - b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} - _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - return -} diff --git a/openstack/evs/extensions/volumeactions/upload_image.go b/openstack/evs/extensions/volumeactions/upload_image.go deleted file mode 100644 index 5b955f544..000000000 --- a/openstack/evs/extensions/volumeactions/upload_image.go +++ /dev/null @@ -1,123 +0,0 @@ -package volumeactions - -import ( - "encoding/json" - "time" - - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" -) - -type UploadImageOpts struct { - // Container format, may be bare, ofv, ova, etc. - ContainerFormat string `json:"container_format,omitempty"` - // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. - DiskFormat string `json:"disk_format,omitempty"` - // The name of image that will be stored in glance. - ImageName string `json:"image_name,omitempty"` - // Force image creation, usable if volume attached to instance. - Force bool `json:"force,omitempty"` -} - -func UploadImage(client *golangsdk.ServiceClient, id string, opts UploadImageOpts) (*VolumeImage, error) { - b, err := golangsdk.BuildRequestBody(opts, "os-volume_upload_image") - if err != nil { - return nil, err - } - - raw, err := client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - if err != nil { - return nil, err - } - - var res struct { - VolumeImage VolumeImage `json:"os-volume_upload_image"` - } - err = extract.Into(raw.Body, &res) - return &res.VolumeImage, err -} - -type VolumeImage struct { - // The ID of a volume an image is created from. - VolumeID string `json:"id"` - // Container format, may be bare, ofv, ova, etc. - ContainerFormat string `json:"container_format"` - // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. - DiskFormat string `json:"disk_format"` - // Human-readable description for the volume. - Description string `json:"display_description"` - // The ID of the created image. - ImageID string `json:"image_id"` - // Human-readable display name for the image. - ImageName string `json:"image_name"` - // Size of the volume in GB. - Size int `json:"size"` - // Current status of the volume. - Status string `json:"status"` - // The date when this volume was last updated. - UpdatedAt time.Time `json:"-"` - // Volume type object of used volume. - VolumeType ImageVolumeType `json:"volume_type"` -} - -func (r *VolumeImage) UnmarshalJSON(b []byte) error { - type tmp VolumeImage - var s struct { - tmp - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = VolumeImage(s.tmp) - - r.UpdatedAt = time.Time(s.UpdatedAt) - return err -} - -type ImageVolumeType struct { - // The ID of a volume type. - ID string `json:"id"` - // Human-readable display name for the volume type. - Name string `json:"name"` - // Human-readable description for the volume type. - Description string `json:"display_description"` - // Flag for public access. - IsPublic bool `json:"is_public"` - // Extra specifications for volume type. - ExtraSpecs map[string]interface{} `json:"extra_specs"` - // ID of quality of service specs. - QosSpecsID string `json:"qos_specs_id"` - // Flag for deletion status of volume type. - Deleted bool `json:"deleted"` - // The date when volume type was deleted. - DeletedAt time.Time `json:"-"` - // The date when volume type was created. - CreatedAt time.Time `json:"-"` - // The date when this volume was last updated. - UpdatedAt time.Time `json:"-"` -} - -func (r *ImageVolumeType) UnmarshalJSON(b []byte) error { - type tmp ImageVolumeType - var s struct { - tmp - CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` - DeletedAt golangsdk.JSONRFC3339MilliNoZ `json:"deleted_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = ImageVolumeType(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) - r.DeletedAt = time.Time(s.DeletedAt) - - return err -} diff --git a/openstack/evs/extensions/volumeactions/urls.go b/openstack/evs/extensions/volumeactions/urls.go new file mode 100644 index 000000000..20486ed71 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/urls.go @@ -0,0 +1,7 @@ +package volumeactions + +import "github.com/gophercloud/gophercloud" + +func actionURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("volumes", id, "action") +} diff --git a/openstack/evs/extensions/volumehost/doc.go b/openstack/evs/extensions/volumehost/doc.go new file mode 100644 index 000000000..6a651a7c5 --- /dev/null +++ b/openstack/evs/extensions/volumehost/doc.go @@ -0,0 +1,26 @@ +/* +Package volumehost provides the ability to extend a volume result with +information about the Openstack host holding the volume. Example: + + type VolumeWithHost struct { + volumes.Volume + volumehost.VolumeHostExt + } + + var allVolumes []VolumeWithHost + + allPages, err := volumes.List(client, nil).AllPages() + if err != nil { + panic("Unable to retrieve volumes: %s", err) + } + + err = volumes.ExtractVolumesInto(allPages, &allVolumes) + if err != nil { + panic("Unable to extract volumes: %s", err) + } + + for _, volume := range allVolumes { + fmt.Println(volume.Host) + } +*/ +package volumehost diff --git a/openstack/evs/extensions/volumehost/results.go b/openstack/evs/extensions/volumehost/results.go new file mode 100644 index 000000000..5434f3772 --- /dev/null +++ b/openstack/evs/extensions/volumehost/results.go @@ -0,0 +1,7 @@ +package volumehost + +// VolumeHostExt is an extension to the base Volume object +type VolumeHostExt struct { + // Host is the identifier of the host holding the volume. + Host string `json:"os-vol-host-attr:host"` +} diff --git a/openstack/evs/extensions/volumetenants/doc.go b/openstack/evs/extensions/volumetenants/doc.go new file mode 100644 index 000000000..2f501649f --- /dev/null +++ b/openstack/evs/extensions/volumetenants/doc.go @@ -0,0 +1,26 @@ +/* +Package volumetenants provides the ability to extend a volume result with +tenant/project information. Example: + + type VolumeWithTenant struct { + volumes.Volume + volumetenants.VolumeTenantExt + } + + var allVolumes []VolumeWithTenant + + allPages, err := volumes.List(client, nil).AllPages() + if err != nil { + panic("Unable to retrieve volumes: %s", err) + } + + err = volumes.ExtractVolumesInto(allPages, &allVolumes) + if err != nil { + panic("Unable to extract volumes: %s", err) + } + + for _, volume := range allVolumes { + fmt.Println(volume.TenantID) + } +*/ +package volumetenants diff --git a/openstack/evs/extensions/volumetenants.go b/openstack/evs/extensions/volumetenants/results.go similarity index 64% rename from openstack/evs/extensions/volumetenants.go rename to openstack/evs/extensions/volumetenants/results.go index 50fdb32b4..821e523b7 100644 --- a/openstack/evs/extensions/volumetenants.go +++ b/openstack/evs/extensions/volumetenants/results.go @@ -1,5 +1,6 @@ -package extensions +package volumetenants +// VolumeTenantExt is an extension to the base Volume object type VolumeTenantExt struct { // TenantID is the id of the project that owns the volume. TenantID string `json:"os-vol-tenant-attr:tenant_id"` diff --git a/openstack/evs/extensions/volumetransfers/doc.go b/openstack/evs/extensions/volumetransfers/doc.go new file mode 100644 index 000000000..fa1ddaa0c --- /dev/null +++ b/openstack/evs/extensions/volumetransfers/doc.go @@ -0,0 +1,65 @@ +/* +Package volumetransfers provides an interaction with volume transfers in the +OpenStack Block Storage service. A volume transfer allows to transfer volumes +between projects withing the same OpenStack region. + +Example to List all Volume Transfer requests being an OpenStack admin + + listOpts := &volumetransfers.ListOpts{ + // this option is available only for OpenStack cloud admin + AllTenants: true, + } + + allPages, err := volumetransfers.List(client, listOpts).AllPages() + if err != nil { + panic(err) + } + + allTransfers, err := volumetransfers.ExtractTransfers(allPages) + if err != nil { + panic(err) + } + + for _, transfer := range allTransfers { + fmt.Println(transfer) + } + +Example to Create a Volume Transfer request + + createOpts := volumetransfers.CreateOpts{ + VolumeID: "uuid", + Name: "my-volume-transfer", + } + + transfer, err := volumetransfers.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(transfer) + // secret auth key is returned only once as a create response + fmt.Printf("AuthKey: %s\n", transfer.AuthKey) + +Example to Accept a Volume Transfer request from the target project + + acceptOpts := volumetransfers.AcceptOpts{ + // see the create response above + AuthKey: "volume-transfer-secret-auth-key", + } + + // see the transfer ID from the create response above + transfer, err := volumetransfers.Accept(client, "transfer-uuid", acceptOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(transfer) + +Example to Delete a Volume Transfer request from the source project + + err := volumetransfers.Delete(client, "transfer-uuid").ExtractErr() + if err != nil { + panic(err) + } +*/ +package volumetransfers diff --git a/openstack/evs/extensions/volumetransfers/requests.go b/openstack/evs/extensions/volumetransfers/requests.go new file mode 100644 index 000000000..d32ac70f8 --- /dev/null +++ b/openstack/evs/extensions/volumetransfers/requests.go @@ -0,0 +1,124 @@ +package volumetransfers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOpts contains options for a Volume transfer. +type CreateOpts struct { + // The ID of the volume to transfer. + VolumeID string `json:"volume_id" required:"true"` + + // The name of the volume transfer + Name string `json:"name,omitempty"` +} + +// ToCreateMap assembles a request body based on the contents of a +// TransferOpts. +func (opts CreateOpts) ToCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "transfer") +} + +// Create will create a volume tranfer request based on the values in CreateOpts. +func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { + b, err := opts.ToCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(transferURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// AcceptOpts contains options for a Volume transfer accept reqeust. +type AcceptOpts struct { + // The auth key of the volume transfer to accept. + AuthKey string `json:"auth_key" required:"true"` +} + +// ToAcceptMap assembles a request body based on the contents of a +// AcceptOpts. +func (opts AcceptOpts) ToAcceptMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "accept") +} + +// Accept will accept a volume tranfer request based on the values in AcceptOpts. +func Accept(client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r CreateResult) { + b, err := opts.ToAcceptMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(acceptURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete deletes a volume transfer. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToTransferListQuery() (string, error) +} + +// ListOpts holds options for listing Transfers. It is passed to the transfers.List +// function. +type ListOpts struct { + // AllTenants will retrieve transfers of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToTransferListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToTransferListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Transfers 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.ToTransferListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return TransferPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves the Transfer with the provided ID. To extract the Transfer object +// from the response, call the Extract method on the GetResult. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/extensions/volumetransfers/results.go b/openstack/evs/extensions/volumetransfers/results.go new file mode 100644 index 000000000..3217174a8 --- /dev/null +++ b/openstack/evs/extensions/volumetransfers/results.go @@ -0,0 +1,102 @@ +package volumetransfers + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Transfer represents a Volume Transfer record +type Transfer struct { + ID string `json:"id"` + AuthKey string `json:"auth_key"` + Name string `json:"name"` + VolumeID string `json:"volume_id"` + CreatedAt time.Time `json:"-"` + Links []map[string]string `json:"links"` +} + +// UnmarshalJSON is our unmarshalling helper +func (r *Transfer) UnmarshalJSON(b []byte) error { + type tmp Transfer + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Transfer(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + + return err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Transfer object out of the commonResult object. +func (r commonResult) Extract() (*Transfer, error) { + var s Transfer + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto converts our response data into a transfer struct +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "transfer") +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} + +// ExtractTransfers extracts and returns Transfers. It is used while iterating over a transfers.List call. +func ExtractTransfers(r pagination.Page) ([]Transfer, error) { + var s []Transfer + err := ExtractTransfersInto(r, &s) + return s, err +} + +// ExtractTransfersInto similar to ExtractInto but operates on a `list` of transfers +func ExtractTransfersInto(r pagination.Page, v interface{}) error { + return r.(TransferPage).Result.ExtractIntoSlicePtr(v, "transfers") +} + +// TransferPage is a pagination.pager that is returned from a call to the List function. +type TransferPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a ListResult contains no Transfers. +func (r TransferPage) IsEmpty() (bool, error) { + transfers, err := ExtractTransfers(r) + return len(transfers) == 0, err +} + +func (page TransferPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"transfers_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} diff --git a/openstack/evs/extensions/volumetransfers/testing/fixtures.go b/openstack/evs/extensions/volumetransfers/testing/fixtures.go new file mode 100644 index 000000000..9714323f9 --- /dev/null +++ b/openstack/evs/extensions/volumetransfers/testing/fixtures.go @@ -0,0 +1,216 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetransfers" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ListOutput = ` +{ + "transfers": [ + { + "created_at": "2020-02-28T12:44:28.051989", + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null + } + ] +} +` + +const GetOutput = ` +{ + "transfer": { + "created_at": "2020-02-28T12:44:28.051989", + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null + } +} +` + +const CreateRequest = ` +{ + "transfer": { + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" + } +} +` + +const CreateResponse = ` +{ + "transfer": { + "auth_key": "cb67e0e7387d9eac", + "created_at": "2020-02-28T12:44:28.051989", + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null, + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" + } +} +` + +const AcceptTransferRequest = ` +{ + "accept": { + "auth_key": "9266c59563c84664" + } +} +` + +const AcceptTransferResponse = ` +{ + "transfer": { + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null, + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" + } +} +` + +var TransferRequest = volumetransfers.CreateOpts{ + VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", +} + +var createdAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-02-28T12:44:28.051989") +var TransferResponse = volumetransfers.Transfer{ + ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + AuthKey: "cb67e0e7387d9eac", + Name: "", + VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", + CreatedAt: createdAt, + Links: []map[string]string{ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self", + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark", + }, + }, +} + +var TransferListResponse = []volumetransfers.Transfer{TransferResponse} + +var AcceptRequest = volumetransfers.AcceptOpts{ + AuthKey: "9266c59563c84664", +} + +var AcceptResponse = volumetransfers.Transfer{ + ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + Name: "", + VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", + Links: []map[string]string{ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self", + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark", + }, + }, +} + +func HandleCreateTransfer(t *testing.T) { + th.Mux.HandleFunc("/os-volume-transfer", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + th.TestJSONRequest(t, r, CreateRequest) + + w.WriteHeader(http.StatusAccepted) + fmt.Fprintf(w, CreateResponse) + }) +} + +func HandleAcceptTransfer(t *testing.T) { + th.Mux.HandleFunc("/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f/accept", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + th.TestJSONRequest(t, r, AcceptTransferRequest) + + w.WriteHeader(http.StatusAccepted) + fmt.Fprintf(w, AcceptTransferResponse) + }) +} + +func HandleDeleteTransfer(t *testing.T) { + th.Mux.HandleFunc("/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + +func HandleListTransfers(t *testing.T) { + th.Mux.HandleFunc("/os-volume-transfer/detail", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + th.TestFormValues(t, r, map[string]string{"all_tenants": "true"}) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListOutput) + }) +} + +func HandleGetTransfer(t *testing.T) { + th.Mux.HandleFunc("/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetOutput) + }) +} diff --git a/openstack/evs/extensions/volumetransfers/testing/requests_test.go b/openstack/evs/extensions/volumetransfers/testing/requests_test.go new file mode 100644 index 000000000..85dc35962 --- /dev/null +++ b/openstack/evs/extensions/volumetransfers/testing/requests_test.go @@ -0,0 +1,90 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetransfers" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateTransfer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateTransfer(t) + + actual, err := volumetransfers.Create(client.ServiceClient(), TransferRequest).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, TransferResponse, *actual) +} + +func TestAcceptTransfer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAcceptTransfer(t) + + actual, err := volumetransfers.Accept(client.ServiceClient(), TransferResponse.ID, AcceptRequest).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, AcceptResponse, *actual) +} + +func TestDeleteTransfer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteTransfer(t) + + err := volumetransfers.Delete(client.ServiceClient(), TransferResponse.ID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestListTransfers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListTransfers(t) + + expectedResponse := TransferListResponse + expectedResponse[0].AuthKey = "" + + count := 0 + err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := volumetransfers.ExtractTransfers(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, expectedResponse, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListTransfersAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListTransfers(t) + + expectedResponse := TransferListResponse + expectedResponse[0].AuthKey = "" + + allPages, err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).AllPages() + th.AssertNoErr(t, err) + actual, err := volumetransfers.ExtractTransfers(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expectedResponse, actual) +} + +func TestGetTransfer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetTransfer(t) + + expectedResponse := TransferResponse + expectedResponse.AuthKey = "" + + actual, err := volumetransfers.Get(client.ServiceClient(), TransferResponse.ID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expectedResponse, *actual) +} diff --git a/openstack/evs/extensions/volumetransfers/urls.go b/openstack/evs/extensions/volumetransfers/urls.go new file mode 100644 index 000000000..cf06dc494 --- /dev/null +++ b/openstack/evs/extensions/volumetransfers/urls.go @@ -0,0 +1,23 @@ +package volumetransfers + +import "github.com/gophercloud/gophercloud" + +func transferURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-volume-transfer") +} + +func acceptURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("os-volume-transfer", id, "accept") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("os-volume-transfer", id) +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-volume-transfer", "detail") +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("os-volume-transfer", id) +} diff --git a/openstack/evs/noauth/new_block_storage_no_auth.go b/openstack/evs/noauth/new_block_storage_no_auth.go deleted file mode 100644 index bc7747811..000000000 --- a/openstack/evs/noauth/new_block_storage_no_auth.go +++ /dev/null @@ -1,48 +0,0 @@ -package noauth - -import ( - "fmt" - "strings" - - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// EndpointOpts specifies a "noauth" Cinder Endpoint. -type EndpointOpts struct { - // CinderEndpoint [required] is currently only used with "noauth" Cinder. - // A cinder endpoint with "auth_strategy=noauth" is necessary, for example: - // http://example.com:8776/v2. - CinderEndpoint string -} - -// NewClient prepares an unauthenticated ProviderClient instance. -func NewClient(options golangsdk.AuthOptions) (*golangsdk.ProviderClient, error) { - if options.Username == "" { - options.Username = "admin" - } - if options.TenantName == "" { - options.TenantName = "admin" - } - - client := &golangsdk.ProviderClient{ - TokenID: fmt.Sprintf("%s:%s", options.Username, options.TenantName), - } - - return client, nil -} - -func NewBlockStorageNoAuth(client *golangsdk.ProviderClient, opts EndpointOpts) (*golangsdk.ServiceClient, error) { - sc := new(golangsdk.ServiceClient) - if opts.CinderEndpoint == "" { - return nil, fmt.Errorf("CinderEndpoint is required") - } - - token := strings.Split(client.TokenID, ":") - if len(token) != 2 { - return nil, fmt.Errorf("Malformed noauth token") - } - - sc.Endpoint = golangsdk.NormalizeURL(fmt.Sprintf("%s%s", golangsdk.NormalizeURL(opts.CinderEndpoint), token[1])) - sc.ProviderClient = client - return sc, nil -} diff --git a/openstack/evs/noauth/testing/fixtures.go b/openstack/evs/noauth/testing/fixtures.go deleted file mode 100644 index f78bda3c5..000000000 --- a/openstack/evs/noauth/testing/fixtures.go +++ /dev/null @@ -1,19 +0,0 @@ -package testing - -// NoAuthResult is the expected result of the noauth Service Client -type NoAuthResult struct { - TokenID string - Endpoint string -} - -var naTestResult = NoAuthResult{ - TokenID: "user:test", - Endpoint: "http://cinder:8776/v2/test/", -} - -var naResult = NoAuthResult{ - TokenID: "admin:admin", - Endpoint: "http://cinder:8776/v2/admin/", -} - -var errorResult = "CinderEndpoint is required" diff --git a/openstack/evs/noauth/testing/requests_test.go b/openstack/evs/noauth/testing/requests_test.go deleted file mode 100644 index 651f16d71..000000000 --- a/openstack/evs/noauth/testing/requests_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/noauth" - - "github.com/opentelekomcloud/gophertelekomcloud" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" -) - -func TestNoAuth(t *testing.T) { - ao := golangsdk.AuthOptions{ - Username: "user", - TenantName: "test", - } - provider, err := noauth.NewClient(ao) - th.AssertNoErr(t, err) - noauthClient, err := noauth.NewBlockStorageNoAuth(provider, noauth.EndpointOpts{ - CinderEndpoint: "http://cinder:8776/v2", - }) - th.AssertNoErr(t, err) - th.AssertEquals(t, naTestResult.Endpoint, noauthClient.Endpoint) - th.AssertEquals(t, naTestResult.TokenID, noauthClient.TokenID) - - ao2 := golangsdk.AuthOptions{} - provider2, err := noauth.NewClient(ao2) - th.AssertNoErr(t, err) - noauthClient2, err := noauth.NewBlockStorageNoAuth(provider2, noauth.EndpointOpts{ - CinderEndpoint: "http://cinder:8776/v2/", - }) - th.AssertNoErr(t, err) - th.AssertEquals(t, naResult.Endpoint, noauthClient2.Endpoint) - th.AssertEquals(t, naResult.TokenID, noauthClient2.TokenID) - - errTest, err := noauth.NewBlockStorageNoAuth(provider2, noauth.EndpointOpts{}) - _ = errTest - if err == nil { - t.Fatalf("Expected to receive error") - } - th.AssertEquals(t, errorResult, err.Error()) -} diff --git a/openstack/evs/v3/attachments/doc.go b/openstack/evs/v3/attachments/doc.go new file mode 100644 index 000000000..838d8962f --- /dev/null +++ b/openstack/evs/v3/attachments/doc.go @@ -0,0 +1,86 @@ +/* +Package attachments provides access to OpenStack Block Storage Attachment +API's. Use of this package requires Cinder version 3.27 at a minimum. + +For more information, see: +https://docs.openstack.org/api-ref/block-storage/v3/index.html#attachments + +Example to List Attachments + + listOpts := &attachments.ListOpts{ + InstanceID: "uuid", + } + + client.Microversion = "3.27" + allPages, err := attachments.List(client, listOpts).AllPages() + if err != nil { + panic(err) + } + + allAttachments, err := attachments.ExtractAttachments(allPages) + if err != nil { + panic(err) + } + + for _, attachment := range allAttachments { + fmt.Println(attachment) + } + +Example to Create Attachment + + createOpts := &attachments.CreateOpts{ + InstanceUUID: "uuid", + VolumeUUID: "uuid" + } + + client.Microversion = "3.27" + attachment, err := attachments.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(attachment) + +Example to Get Attachment + + client.Microversion = "3.27" + attachment, err := attachments.Get(client, "uuid").Extract() + if err != nil { + panic(err) + } + + fmt.Println(attachment) + +Example to Update Attachment + + opts := &attachments.UpdateOpts{ + Connector: map[string]interface{}{ + "mode": "ro", + } + } + + client.Microversion = "3.27" + attachment, err := attachments.Update(client, "uuid", opts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(attachment) + +Example to Complete Attachment + + client.Microversion = "3.44" + err := attachments.Complete(client, "uuid").ExtractErr() + if err != nil { + panic(err) + } + +Example to Delete Attachment + + client.Microversion = "3.27" + err := attachments.Delete(client, "uuid").ExtractErr() + if err != nil { + panic(err) + } +*/ +package attachments diff --git a/openstack/evs/v3/attachments/requests.go b/openstack/evs/v3/attachments/requests.go new file mode 100644 index 000000000..b6032a3b0 --- /dev/null +++ b/openstack/evs/v3/attachments/requests.go @@ -0,0 +1,180 @@ +package attachments + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToAttachmentCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for creating a Volume attachment. This object is +// passed to the Create function. For more information about these parameters, +// see the Attachment object. +type CreateOpts struct { + // VolumeUUID is the UUID of the Cinder volume to create the attachment + // record for. + VolumeUUID string `json:"volume_uuid"` + // InstanceUUID is the ID of the Server to create the attachment for. + // When attaching to a Nova Server this is the Nova Server (Instance) + // UUID. + InstanceUUID string `json:"instance_uuid"` + // Connector is an optional map containing all of the needed atachment + // information for exmaple initiator IQN, etc. + Connector map[string]interface{} `json:"connector,omitempty"` + // Mode is an attachment mode. Acceptable values are read-only ('ro') + // and read-and-write ('rw'). Available only since 3.54 microversion. + // For APIs from 3.27 till 3.53 use Connector["mode"] = "rw|ro". + Mode string `json:"mode,omitempty"` +} + +// ToAttachmentCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToAttachmentCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "attachment") +} + +// Create will create a new Attachment based on the values in CreateOpts. To +// extract the Attachment object from the response, call the Extract method on +// the CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToAttachmentCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete will delete the existing Attachment with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(deleteURL(client, id), &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get retrieves the Attachment with the provided ID. To extract the Attachment +// object from the response, call the Extract method on the GetResult. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToAttachmentListQuery() (string, error) +} + +// ListOpts holds options for listing Attachments. It is passed to the attachments.List +// function. +type ListOpts struct { + // AllTenants will retrieve attachments of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Status will filter by the specified status. + Status string `q:"status"` + + // ProjectID will filter by a specific tenant/project ID. + ProjectID string `q:"project_id"` + + // VolumeID will filter by a specific volume ID. + VolumeID string `q:"volume_id"` + + // InstanceID will filter by a specific instance ID. + InstanceID string `q:"instance_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToAttachmentListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToAttachmentListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Attachments 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.ToAttachmentListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AttachmentPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToAttachmentUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Attachment. +// This is used to finalize an attachment that was created without a +// connector (reserve). +type UpdateOpts struct { + Connector map[string]interface{} `json:"connector"` +} + +// ToAttachmentUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToAttachmentUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "attachment") +} + +// Update will update the Attachment with provided information. To extract the +// updated Attachment from the response, call the Extract method on the +// UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToAttachmentUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Complete will complete an attachment for a cinder volume. +// Available starting in the 3.44 microversion. +func Complete(client *gophercloud.ServiceClient, id string) (r CompleteResult) { + b := map[string]interface{}{ + "os-complete": nil, + } + resp, err := client.Post(completeURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/attachments/results.go b/openstack/evs/v3/attachments/results.go new file mode 100644 index 000000000..0a97730ed --- /dev/null +++ b/openstack/evs/v3/attachments/results.go @@ -0,0 +1,120 @@ +// Package attachments provides access to OpenStack Block Storage Attachment +// API's. Use of this package requires Cinder version 3.27 at a minimum. +package attachments + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Attachment contains all the information associated with an OpenStack +// Attachment. +type Attachment struct { + // ID is the Unique identifier for the attachment. + ID string `json:"id"` + // VolumeID is the UUID of the Volume associated with this attachment. + VolumeID string `json:"volume_id"` + // Instance is the Instance/Server UUID associated with this attachment. + Instance string `json:"instance"` + // AttachedAt is the time the attachment was created. + AttachedAt time.Time `json:"-"` + // DetachedAt is the time the attachment was detached. + DetachedAt time.Time `json:"-"` + // Status is the current attach status. + Status string `json:"status"` + // AttachMode includes things like Read Only etc. + AttachMode string `json:"attach_mode"` + // ConnectionInfo is the required info for a node to make a connection + // provided by the driver. + ConnectionInfo map[string]interface{} `json:"connection_info"` +} + +// UnmarshalJSON is our unmarshalling helper +func (r *Attachment) UnmarshalJSON(b []byte) error { + type tmp Attachment + var s struct { + tmp + AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` + DetachedAt gophercloud.JSONRFC3339MilliNoZ `json:"detached_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Attachment(s.tmp) + + r.AttachedAt = time.Time(s.AttachedAt) + r.DetachedAt = time.Time(s.DetachedAt) + + return err +} + +// AttachmentPage is a pagination.pager that is returned from a call to the List +// function. +type AttachmentPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a ListResult contains no Attachments. +func (r AttachmentPage) IsEmpty() (bool, error) { + attachments, err := ExtractAttachments(r) + return len(attachments) == 0, err +} + +// ExtractAttachments extracts and returns Attachments. It is used while +// iterating over a attachment.List call. +func ExtractAttachments(r pagination.Page) ([]Attachment, error) { + var s []Attachment + err := ExtractAttachmentsInto(r, &s) + return s, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Attachment object out of the commonResult object. +func (r commonResult) Extract() (*Attachment, error) { + var s Attachment + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto converts our response data into a attachment struct. +func (r commonResult) ExtractInto(a interface{}) error { + return r.Result.ExtractIntoStructPtr(a, "attachment") +} + +// ExtractAttachmentsInto similar to ExtractInto but operates on a List of +// attachments. +func ExtractAttachmentsInto(r pagination.Page, a interface{}) error { + return r.(AttachmentPage).Result.ExtractIntoSlicePtr(a, "attachments") +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} + +// CompleteResult contains the response body and error from a Complete request. +type CompleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/evs/v3/attachments/testing/fixtures.go b/openstack/evs/v3/attachments/testing/fixtures.go new file mode 100644 index 000000000..b19245e57 --- /dev/null +++ b/openstack/evs/v3/attachments/testing/fixtures.go @@ -0,0 +1,233 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +var ( + attachedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2015-09-16T09:28:52.000000") + detachedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2015-09-16T09:28:52.000000") + expectedAttachment = &attachments.Attachment{ + ID: "05551600-a936-4d4a-ba42-79a037c1-c91a", + VolumeID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Instance: "83ec2e3b-4321-422b-8706-a84185f52a0a", + AttachMode: "rw", + Status: "attaching", + AttachedAt: attachedAt, + DetachedAt: detachedAt, + ConnectionInfo: map[string]interface{}{}, + } +) + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/detail", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ` + { + "attachments": [ + { + "status": "attaching", + "detached_at": "2015-09-16T09:28:52.000000", + "connection_info": {}, + "attached_at": "2015-09-16T09:28:52.000000", + "attach_mode": "rw", + "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" + } + ], + "attachments_links": [ + { + "href": "%s/attachments/detail?marker=1", + "rel": "next" + } + ] +} + `, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"volumes": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", 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, ` +{ + "attachment": { + "status": "attaching", + "detached_at": "2015-09-16T09:28:52.000000", + "connection_info": {}, + "attached_at": "2015-09-16T09:28:52.000000", + "attach_mode": "rw", + "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" + } +} + `) + }) +} + +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments", 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, ` +{ + "attachment": { + "instance_uuid": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "connector": { + "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", + "ip": "192.168.1.20", + "platform": "x86_64", + "host": "tempest-1", + "os_type": "linux2", + "multipath": false, + "mountpoint": "/dev/vdb", + "mode": "rw" + }, + "volume_uuid": "289da7f8-6440-407c-9fb4-7db01ec49164" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "attachment": { + "status": "attaching", + "detached_at": "2015-09-16T09:28:52.000000", + "connection_info": {}, + "attached_at": "2015-09-16T09:28:52.000000", + "attach_mode": "rw", + "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" + } +} + `) + }) +} + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusOK) + }) +} + +func MockUpdateResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + th.TestJSONRequest(t, r, ` +{ + "attachment": { + "connector": { + "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", + "ip": "192.168.1.20", + "platform": "x86_64", + "host": "tempest-1", + "os_type": "linux2", + "multipath": false, + "mountpoint": "/dev/vdb", + "mode": "rw" + } + } +} + `) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` +{ + "attachment": { + "status": "attaching", + "detached_at": "2015-09-16T09:28:52.000000", + "connection_info": {}, + "attached_at": "2015-09-16T09:28:52.000000", + "attach_mode": "rw", + "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" + } +} + `) + }) +} + +func MockUpdateEmptyResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + th.TestJSONRequest(t, r, ` +{ + "attachment": { + "connector": null + } +} + `) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` +{ + "attachment": { + "status": "attaching", + "detached_at": "2015-09-16T09:28:52.000000", + "connection_info": {}, + "attached_at": "2015-09-16T09:28:52.000000", + "attach_mode": "rw", + "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" + } +} + `) + }) +} + +var completeRequest = ` +{ + "os-complete": null +} +` + +func MockCompleteResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, completeRequest) + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/evs/v3/attachments/testing/requests_test.go b/openstack/evs/v3/attachments/testing/requests_test.go new file mode 100644 index 000000000..2c9793100 --- /dev/null +++ b/openstack/evs/v3/attachments/testing/requests_test.go @@ -0,0 +1,119 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allPages, err := attachments.List(client.ServiceClient(), &attachments.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := attachments.ExtractAttachments(allPages) + th.AssertNoErr(t, err) + + expected := []attachments.Attachment{*expectedAttachment} + + th.CheckDeepEquals(t, expected, actual) + +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + attachment, err := attachments.Get(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, expectedAttachment, attachment) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := &attachments.CreateOpts{ + InstanceUUID: "83ec2e3b-4321-422b-8706-a84185f52a0a", + Connector: map[string]interface{}{ + "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", + "ip": "192.168.1.20", + "platform": "x86_64", + "host": "tempest-1", + "os_type": "linux2", + "multipath": false, + "mountpoint": "/dev/vdb", + "mode": "rw", + }, + VolumeUUID: "289da7f8-6440-407c-9fb4-7db01ec49164", + } + attachment, err := attachments.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, expectedAttachment, attachment) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := attachments.Delete(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a") + th.AssertNoErr(t, res.Err) +} + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateResponse(t) + + options := &attachments.UpdateOpts{ + Connector: map[string]interface{}{ + "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", + "ip": "192.168.1.20", + "platform": "x86_64", + "host": "tempest-1", + "os_type": "linux2", + "multipath": false, + "mountpoint": "/dev/vdb", + "mode": "rw", + }, + } + attachment, err := attachments.Update(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expectedAttachment, attachment) +} + +func TestUpdateEmpty(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateEmptyResponse(t) + + options := attachments.UpdateOpts{} + attachment, err := attachments.Update(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expectedAttachment, attachment) +} + +func TestComplete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCompleteResponse(t) + + err := attachments.Complete(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/evs/v3/attachments/urls.go b/openstack/evs/v3/attachments/urls.go new file mode 100644 index 000000000..a774d7772 --- /dev/null +++ b/openstack/evs/v3/attachments/urls.go @@ -0,0 +1,27 @@ +package attachments + +import "github.com/gophercloud/gophercloud" + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("attachments") +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("attachments", "detail") +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("attachments", id) +} + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("attachments", id) +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("attachments", id) +} + +func completeURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("attachments", id, "action") +} diff --git a/openstack/evs/v3/attachments/util.go b/openstack/evs/v3/attachments/util.go new file mode 100644 index 000000000..dca1b888e --- /dev/null +++ b/openstack/evs/v3/attachments/util.go @@ -0,0 +1,22 @@ +package attachments + +import ( + "github.com/gophercloud/gophercloud" +) + +// WaitForStatus will continually poll the resource, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { + return gophercloud.WaitFor(secs, func() (bool, error) { + current, err := Get(c, id).Extract() + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} diff --git a/openstack/evs/v3/qos/doc.go b/openstack/evs/v3/qos/doc.go new file mode 100644 index 000000000..86f288fa5 --- /dev/null +++ b/openstack/evs/v3/qos/doc.go @@ -0,0 +1,146 @@ +/* +Package qos provides information and interaction with the QoS specifications +for the Openstack Blockstorage service. + +Example to create a QoS specification + + createOpts := qos.CreateOpts{ + Name: "test", + Consumer: qos.ConsumerFront, + Specs: map[string]string{ + "read_iops_sec": "20000", + }, + } + + test, err := qos.Create(client, createOpts).Extract() + if err != nil { + log.Fatal(err) + } + + fmt.Printf("QoS: %+v\n", test) + +Example to delete a QoS specification + + qosID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" + + deleteOpts := qos.DeleteOpts{ + Force: false, + } + + err = qos.Delete(client, qosID, deleteOpts).ExtractErr() + if err != nil { + log.Fatal(err) + } + +Example to list QoS specifications + + listOpts := qos.ListOpts{} + + allPages, err := qos.List(client, listOpts).AllPages() + if err != nil { + panic(err) + } + + allQoS, err := qos.ExtractQoS(allPages) + if err != nil { + panic(err) + } + + for _, qos := range allQoS { + fmt.Printf("List: %+v\n", qos) + } + +Example to get a single QoS specification + + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + + singleQos, err := qos.Get(client, test.ID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("Get: %+v\n", singleQos) + +Example of updating QoSSpec + + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + + updateOpts := qos.UpdateOpts{ + Consumer: qos.ConsumerBack, + Specs: map[string]string{ + "read_iops_sec": "40000", + }, + } + + specs, err := qos.Update(client, qosID, updateOpts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", specs) + +Example of deleting specific keys/specs from a QoS + + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + + keysToDelete := qos.DeleteKeysOpts{"read_iops_sec"} + err = qos.DeleteKeys(client, qosID, keysToDelete).ExtractErr() + if err != nil { + panic(err) + } + +Example of associating a QoS with a volume type + + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + volID := "b596be6a-0ce9-43fa-804a-5c5e181ede76" + + associateOpts := qos.AssociateOpts{ + VolumeTypeID: volID, + } + + err = qos.Associate(client, qosID, associateOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of disassociating a QoS from a volume type + + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + volID := "b596be6a-0ce9-43fa-804a-5c5e181ede76" + + disassociateOpts := qos.DisassociateOpts{ + VolumeTypeID: volID, + } + + err = qos.Disassociate(client, qosID, disassociateOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of disaassociating a Qos from all volume types + + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + + err = qos.DisassociateAll(client, qosID).ExtractErr() + if err != nil { + panic(err) + } + +Example of listing all associations of a QoS + + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + + allQosAssociations, err := qos.ListAssociations(client, qosID).AllPages() + if err != nil { + panic(err) + } + + allAssociations, err := qos.ExtractAssociations(allQosAssociations) + if err != nil { + panic(err) + } + + for _, association := range allAssociations { + fmt.Printf("Association: %+v\n", association) + } +*/ +package qos diff --git a/openstack/evs/v3/qos/requests.go b/openstack/evs/v3/qos/requests.go new file mode 100644 index 000000000..8169f8219 --- /dev/null +++ b/openstack/evs/v3/qos/requests.go @@ -0,0 +1,328 @@ +package qos + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type CreateOptsBuilder interface { + ToQoSCreateMap() (map[string]interface{}, error) +} + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToQoSListQuery() (string, error) +} + +type QoSConsumer string + +const ( + ConsumerFront QoSConsumer = "front-end" + ConsumerBack QoSConsumer = "back-end" + ConsumerBoth QoSConsumer = "both" +) + +// CreateOpts contains options for creating a QoS specification. +// This object is passed to the qos.Create function. +type CreateOpts struct { + // The name of the QoS spec + Name string `json:"name"` + // The consumer of the QoS spec. Possible values are + // both, front-end, back-end. + Consumer QoSConsumer `json:"consumer,omitempty"` + // Specs is a collection of miscellaneous key/values used to set + // specifications for the QoS + Specs map[string]string `json:"-"` +} + +// ToQoSCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToQoSCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "qos_specs") + if err != nil { + return nil, err + } + + if opts.Specs != nil { + if v, ok := b["qos_specs"].(map[string]interface{}); ok { + for key, value := range opts.Specs { + v[key] = value + } + } + } + + return b, nil +} + +// Create will create a new QoS based on the values in CreateOpts. To extract +// the QoS object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToQoSCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteOptsBuilder allows extensions to add additional parameters to the +// Delete request. +type DeleteOptsBuilder interface { + ToQoSDeleteQuery() (string, error) +} + +// DeleteOpts contains options for deleting a QoS. This object is passed to +// the qos.Delete function. +type DeleteOpts struct { + // Delete a QoS specification even if it is in-use + Force bool `q:"force"` +} + +// ToQoSDeleteQuery formats a DeleteOpts into a query string. +func (opts DeleteOpts) ToQoSDeleteQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Delete will delete the existing QoS with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { + url := deleteURL(client, id) + if opts != nil { + query, err := opts.ToQoSDeleteQuery() + if err != nil { + r.Err = err + return + } + url += query + } + resp, err := client.Delete(url, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +type ListOpts struct { + // Sort is Comma-separated list of sort keys and optional sort + // directions in the form of < key > [: < direction > ]. A valid + //direction is asc (ascending) or desc (descending). + Sort string `q:"sort"` + + // Marker and Limit control paging. + // Marker instructs List where to start listing from. + Marker string `q:"marker"` + + // Limit instructs List to refrain from sending excessively large lists of + // QoS. + Limit int `q:"limit"` +} + +// ToQoSListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToQoSListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List instructs OpenStack to provide a list of QoS. +// You may provide criteria by which List curtails its results for easier +// processing. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToQoSListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return QoSPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details of a single qos. Use Extract to convert its +// result into a QoS. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateQosSpecsOptsBuilder allows extensions to add additional parameters to the +// CreateQosSpecs requests. +type CreateQosSpecsOptsBuilder interface { + ToQosSpecsCreateMap() (map[string]interface{}, error) +} + +// UpdateOpts contains options for creating a QoS specification. +// This object is passed to the qos.Update function. +type UpdateOpts struct { + // The consumer of the QoS spec. Possible values are + // both, front-end, back-end. + Consumer QoSConsumer `json:"consumer,omitempty"` + // Specs is a collection of miscellaneous key/values used to set + // specifications for the QoS + Specs map[string]string `json:"-"` +} + +type UpdateOptsBuilder interface { + ToQoSUpdateMap() (map[string]interface{}, error) +} + +// ToQoSUpdateMap assembles a request body based on the contents of a +// UpdateOpts. +func (opts UpdateOpts) ToQoSUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "qos_specs") + if err != nil { + return nil, err + } + + if opts.Specs != nil { + if v, ok := b["qos_specs"].(map[string]interface{}); ok { + for key, value := range opts.Specs { + v[key] = value + } + } + } + + return b, nil +} + +// Update will update an existing QoS based on the values in UpdateOpts. +// To extract the QoS object from the response, call the Extract method +// on the UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r updateResult) { + b, err := opts.ToQoSUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteKeysOptsBuilder allows extensions to add additional parameters to the +// CreateExtraSpecs requests. +type DeleteKeysOptsBuilder interface { + ToDeleteKeysCreateMap() (map[string]interface{}, error) +} + +// DeleteKeysOpts is a string slice that contains keys to be deleted. +type DeleteKeysOpts []string + +// ToDeleteKeysCreateMap assembles a body for a Create request based on +// the contents of ExtraSpecsOpts. +func (opts DeleteKeysOpts) ToDeleteKeysCreateMap() (map[string]interface{}, error) { + return map[string]interface{}{"keys": opts}, nil +} + +// DeleteKeys will delete the keys/specs from the specified QoS +func DeleteKeys(client *gophercloud.ServiceClient, qosID string, opts DeleteKeysOptsBuilder) (r DeleteResult) { + b, err := opts.ToDeleteKeysCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(deleteKeysURL(client, qosID), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// AssociateOpitsBuilder allows extensions to define volume type id +// to the associate query +type AssociateOptsBuilder interface { + ToQosAssociateQuery() (string, error) +} + +// AssociateOpts contains options for associating a QoS with a +// volume type +type AssociateOpts struct { + VolumeTypeID string `q:"vol_type_id" required:"true"` +} + +// ToQosAssociateQuery formats an AssociateOpts into a query string +func (opts AssociateOpts) ToQosAssociateQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Associate will associate a qos with a volute type +func Associate(client *gophercloud.ServiceClient, qosID string, opts AssociateOptsBuilder) (r AssociateResult) { + url := associateURL(client, qosID) + query, err := opts.ToQosAssociateQuery() + if err != nil { + r.Err = err + return + } + url += query + + resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DisassociateOpitsBuilder allows extensions to define volume type id +// to the disassociate query +type DisassociateOptsBuilder interface { + ToQosDisassociateQuery() (string, error) +} + +// DisassociateOpts contains options for disassociating a QoS from a +// volume type +type DisassociateOpts struct { + VolumeTypeID string `q:"vol_type_id" required:"true"` +} + +// ToQosDisassociateQuery formats a DisassociateOpts into a query string +func (opts DisassociateOpts) ToQosDisassociateQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Disassociate will disassociate a qos from a volute type +func Disassociate(client *gophercloud.ServiceClient, qosID string, opts DisassociateOptsBuilder) (r DisassociateResult) { + url := disassociateURL(client, qosID) + query, err := opts.ToQosDisassociateQuery() + if err != nil { + r.Err = err + return + } + url += query + + resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DisassociateAll will disassociate a qos from all volute types +func DisassociateAll(client *gophercloud.ServiceClient, qosID string) (r DisassociateAllResult) { + resp, err := client.Get(disassociateAllURL(client, qosID), nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListAssociations retrieves the associations of a QoS. +func ListAssociations(client *gophercloud.ServiceClient, qosID string) pagination.Pager { + url := listAssociationsURL(client, qosID) + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AssociationPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/evs/v3/qos/results.go b/openstack/evs/v3/qos/results.go new file mode 100644 index 000000000..34008aed8 --- /dev/null +++ b/openstack/evs/v3/qos/results.go @@ -0,0 +1,144 @@ +package qos + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// QoS contains all the information associated with an OpenStack QoS specification. +type QoS struct { + // Name is the name of the QoS. + Name string `json:"name"` + // Unique identifier for the QoS. + ID string `json:"id"` + // Consumer of QoS + Consumer string `json:"consumer"` + // Arbitrary key-value pairs defined by the user. + Specs map[string]string `json:"specs"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the QoS object out of the commonResult object. +func (r commonResult) Extract() (*QoS, error) { + var s QoS + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto converts our response data into a QoS struct +func (r commonResult) ExtractInto(qos interface{}) error { + return r.Result.ExtractIntoStructPtr(qos, "qos_specs") +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} + +type QoSPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines if a QoSPage contains any results. +func (page QoSPage) IsEmpty() (bool, error) { + qos, err := ExtractQoS(page) + return len(qos) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (page QoSPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"qos_specs_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractQoS provides access to the list of qos in a page acquired +// from the List operation. +func ExtractQoS(r pagination.Page) ([]QoS, error) { + var s struct { + QoSs []QoS `json:"qos_specs"` + } + err := (r.(QoSPage)).ExtractInto(&s) + return s.QoSs, err +} + +// GetResult is the response of a Get operations. Call its Extract method to +// interpret it as a Flavor. +type GetResult struct { + commonResult +} + +// Extract interprets any updateResult as qosSpecs, if possible. +func (r updateResult) Extract() (map[string]string, error) { + var s struct { + QosSpecs map[string]string `json:"qos_specs"` + } + err := r.ExtractInto(&s) + return s.QosSpecs, err +} + +// updateResult contains the result of a call for (potentially) multiple +// key-value pairs. Call its Extract method to interpret it as a +// map[string]interface. +type updateResult struct { + gophercloud.Result +} + +// AssociateResult contains the response body and error from a Associate request. +type AssociateResult struct { + gophercloud.ErrResult +} + +// DisassociateResult contains the response body and error from a Disassociate request. +type DisassociateResult struct { + gophercloud.ErrResult +} + +// DisassociateAllResult contains the response body and error from a DisassociateAll request. +type DisassociateAllResult struct { + gophercloud.ErrResult +} + +// QoS contains all the information associated with an OpenStack QoS specification. +type QosAssociation struct { + // Name is the name of the associated resource + Name string `json:"name"` + // Unique identifier of the associated resources + ID string `json:"id"` + // AssociationType of the QoS Association + AssociationType string `json:"association_type"` +} + +// AssociationPage contains a single page of all Associations of a QoS +type AssociationPage struct { + pagination.SinglePageBase +} + +// IsEmpty indicates whether an Association page is empty. +func (page AssociationPage) IsEmpty() (bool, error) { + v, err := ExtractAssociations(page) + return len(v) == 0, err +} + +// ExtractAssociations interprets a page of results as a slice of QosAssociations +func ExtractAssociations(r pagination.Page) ([]QosAssociation, error) { + var s struct { + QosAssociations []QosAssociation `json:"qos_associations"` + } + err := (r.(AssociationPage)).ExtractInto(&s) + return s.QosAssociations, err +} diff --git a/openstack/evs/v3/qos/testing/doc.go b/openstack/evs/v3/qos/testing/doc.go new file mode 100644 index 000000000..0155a0963 --- /dev/null +++ b/openstack/evs/v3/qos/testing/doc.go @@ -0,0 +1,2 @@ +// Package testing for qos_v3 +package testing diff --git a/openstack/evs/v3/qos/testing/fixtures.go b/openstack/evs/v3/qos/testing/fixtures.go new file mode 100644 index 000000000..d3bf00d69 --- /dev/null +++ b/openstack/evs/v3/qos/testing/fixtures.go @@ -0,0 +1,232 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +var createQoSExpected = qos.QoS{ + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + Name: "qos-001", + Consumer: "front-end", + Specs: map[string]string{ + "read_iops_sec": "20000", + }, +} + +var getQoSExpected = qos.QoS{ + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + Name: "qos-001", + Consumer: "front-end", + Specs: map[string]string{ + "read_iops_sec": "20000", + }, +} + +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs", 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, ` +{ + "qos_specs": { + "name": "qos-001", + "consumer": "front-end", + "read_iops_sec": "20000" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "qos_specs": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "qos-001", + "consumer": "front-end", + "specs": { + "read_iops_sec": "20000" + } + } +} + `) + }) +} + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs", 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") + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ` + { + "qos_specs": [ + { + "consumer": "back-end", + "id": "1", + "name": "foo", + "specs": {} + }, + { + "consumer": "front-end", + "id": "2", + "name": "bar", + "specs" : { + "read_iops_sec" : "20000" + } + } + + ], + "qos_specs_links": [ + { + "href": "%s/qos-specs?marker=2", + "rel": "next" + } + ] + } + `, th.Server.URL) + case "2": + fmt.Fprintf(w, `{ "qos_specs": [] }`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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, ` +{ + "qos_specs": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "qos-001", + "consumer": "front-end", + "specs": { + "read_iops_sec": "20000" + } + } +} + `) + }) +} + +// UpdateBody provides a PUT result of the qos_specs for a QoS +const UpdateBody = ` +{ + "qos_specs" : { + "consumer": "back-end", + "read_iops_sec": "40000", + "write_iops_sec": "40000" + } +} +` + +// UpdateQos is the expected qos_specs returned from PUT on a qos's qos_specs +var UpdateQos = map[string]string{ + "consumer": "back-end", + "read_iops_sec": "40000", + "write_iops_sec": "40000", +} + +func MockUpdateResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, `{ + "qos_specs": { + "consumer": "back-end", + "read_iops_sec": "40000", + "write_iops_sec": "40000" + } + }`) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdateBody) + }) +} + +func MockDeleteKeysResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/delete_keys", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, `{ + "keys": [ + "read_iops_sec" + ] + }`) + + w.WriteHeader(http.StatusAccepted) + }) +} + +func MockAssociateResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/associate", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} + +func MockDisassociateResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/disassociate", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} + +func MockDisassociateAllResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/disassociate_all", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} + +func MockListAssociationsResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/associations", 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") + fmt.Fprintf(w, ` + { + "qos_associations": [ + { + "name": "foo", + "id": "2f954bcf047c4ee9b09a37d49ae6db54", + "association_type": "volume_type" + } + ] + } + `) + }) +} diff --git a/openstack/evs/v3/qos/testing/requests_test.go b/openstack/evs/v3/qos/testing/requests_test.go new file mode 100644 index 000000000..e452fa2ee --- /dev/null +++ b/openstack/evs/v3/qos/testing/requests_test.go @@ -0,0 +1,178 @@ +package testing + +import ( + "reflect" + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := qos.CreateOpts{ + Name: "qos-001", + Consumer: qos.ConsumerFront, + Specs: map[string]string{ + "read_iops_sec": "20000", + }, + } + actual, err := qos.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &createQoSExpected, actual) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := qos.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteOpts{}) + th.AssertNoErr(t, res.Err) +} + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + pages := 0 + err := qos.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + pages++ + actual, err := qos.ExtractQoS(page) + if err != nil { + return false, err + } + + expected := []qos.QoS{ + {ID: "1", Consumer: "back-end", Name: "foo", Specs: map[string]string{}}, + {ID: "2", Consumer: "front-end", Name: "bar", Specs: map[string]string{ + "read_iops_sec": "20000", + }, + }, + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } + + return true, nil + }) + if err != nil { + t.Fatal(err) + } + if pages != 1 { + t.Errorf("Expected one page, got %d", pages) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + actual, err := qos.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &getQoSExpected, actual) +} + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + MockUpdateResponse(t) + + updateOpts := qos.UpdateOpts{ + Consumer: qos.ConsumerBack, + Specs: map[string]string{ + "read_iops_sec": "40000", + "write_iops_sec": "40000", + }, + } + + expected := UpdateQos + actual, err := qos.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestDeleteKeys(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteKeysResponse(t) + + res := qos.DeleteKeys(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteKeysOpts{"read_iops_sec"}) + th.AssertNoErr(t, res.Err) +} + +func TestAssociate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockAssociateResponse(t) + + associateOpts := qos.AssociateOpts{ + VolumeTypeID: "b596be6a-0ce9-43fa-804a-5c5e181ede76", + } + + res := qos.Associate(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", associateOpts) + th.AssertNoErr(t, res.Err) +} + +func TestDisssociate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDisassociateResponse(t) + + disassociateOpts := qos.DisassociateOpts{ + VolumeTypeID: "b596be6a-0ce9-43fa-804a-5c5e181ede76", + } + + res := qos.Disassociate(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", disassociateOpts) + th.AssertNoErr(t, res.Err) +} + +func TestDissasociateAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDisassociateAllResponse(t) + + res := qos.DisassociateAll(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} + +func TestQosAssociationsList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListAssociationsResponse(t) + + expected := []qos.QosAssociation{ + { + Name: "foo", + ID: "2f954bcf047c4ee9b09a37d49ae6db54", + AssociationType: "volume_type", + }, + } + + allPages, err := qos.ListAssociations(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").AllPages() + th.AssertNoErr(t, err) + + actual, err := qos.ExtractAssociations(allPages) + th.AssertNoErr(t, err) + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} diff --git a/openstack/evs/v3/qos/urls.go b/openstack/evs/v3/qos/urls.go new file mode 100644 index 000000000..e0e4a0eec --- /dev/null +++ b/openstack/evs/v3/qos/urls.go @@ -0,0 +1,43 @@ +package qos + +import "github.com/gophercloud/gophercloud" + +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id) +} + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("qos-specs") +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("qos-specs") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("qos-specs", id) +} + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id) +} + +func deleteKeysURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id, "delete_keys") +} + +func associateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id, "associate") +} + +func disassociateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id, "disassociate") +} + +func disassociateAllURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id, "disassociate_all") +} + +func listAssociationsURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id, "associations") +} diff --git a/openstack/evs/v3/snapshots/doc.go b/openstack/evs/v3/snapshots/doc.go new file mode 100644 index 000000000..702094c3d --- /dev/null +++ b/openstack/evs/v3/snapshots/doc.go @@ -0,0 +1,61 @@ +/* +Package snapshots provides information and interaction with snapshots in the +OpenStack Block Storage service. A snapshot is a point in time copy of the +data contained in an external storage volume, and can be controlled +programmatically. + +Example to list Snapshots + + allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() + if err != nil{ + panic(err) + } + snapshots, err := snapshots.ExtractSnapshots(allPages) + if err != nil{ + panic(err) + } + for _,s := range snapshots{ + fmt.Println(s) + } + +Example to get a Snapshot + + snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" + snapshot, err := snapshots.Get(client, snapshotID).Extract() + if err != nil{ + panic(err) + } + fmt.Println(snapshot) + +Example to create a Snapshot + + snapshot, err := snapshots.Create(client, snapshots.CreateOpts{ + Name:"snapshot_001", + VolumeID:"5aa119a8-d25b-45a7-8d1b-88e127885635", + }).Extract() + if err != nil{ + panic(err) + } + fmt.Println(snapshot) + +Example to delete a Snapshot + + snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" + err := snapshots.Delete(client, snapshotID).ExtractErr() + if err != nil{ + panic(err) + } + +Example to update a Snapshot + + snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" + snapshot, err = snapshots.Update(client, snapshotID, snapshots.UpdateOpts{ + Name: "snapshot_002", + Description:"description_002", + }).Extract() + if err != nil{ + panic(err) + } + fmt.Println(snapshot) +*/ +package snapshots diff --git a/openstack/evs/v3/snapshots/requests.go b/openstack/evs/v3/snapshots/requests.go new file mode 100644 index 000000000..7dcfed794 --- /dev/null +++ b/openstack/evs/v3/snapshots/requests.go @@ -0,0 +1,191 @@ +package snapshots + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToSnapshotCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for creating a Snapshot. This object is passed to +// the snapshots.Create function. For more information about these parameters, +// see the Snapshot object. +type CreateOpts struct { + VolumeID string `json:"volume_id" required:"true"` + Force bool `json:"force,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` +} + +// ToSnapshotCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "snapshot") +} + +// Create will create a new Snapshot based on the values in CreateOpts. To +// extract the Snapshot object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToSnapshotCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete will delete the existing Snapshot with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get retrieves the Snapshot with the provided ID. To extract the Snapshot +// object from the response, call the Extract method on the GetResult. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToSnapshotListQuery() (string, error) +} + +// ListOpts holds options for listing Snapshots. It is passed to the snapshots.List +// function. +type ListOpts struct { + // AllTenants will retrieve snapshots of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Name will filter by the specified snapshot name. + Name string `q:"name"` + + // Status will filter by the specified status. + Status string `q:"status"` + + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required to use this. + TenantID string `q:"project_id"` + + // VolumeID will filter by a specified volume ID. + VolumeID string `q:"volume_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToSnapshotListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToSnapshotListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Snapshots 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.ToSnapshotListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return SnapshotPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToSnapshotUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Snapshot. This object is passed +// to the snapshots.Update function. For more information about the parameters, see +// the Snapshot object. +type UpdateOpts struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` +} + +// ToSnapshotUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "snapshot") +} + +// Update will update the Snapshot with provided information. To extract the updated +// Snapshot from the response, call the Extract method on the UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToSnapshotUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateMetadataOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateMetadataOptsBuilder interface { + ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) +} + +// UpdateMetadataOpts contain options for updating an existing Snapshot. This +// object is passed to the snapshots.Update function. For more information +// about the parameters, see the Snapshot object. +type UpdateMetadataOpts struct { + Metadata map[string]interface{} `json:"metadata,omitempty"` +} + +// ToSnapshotUpdateMetadataMap assembles a request body based on the contents of +// an UpdateMetadataOpts. +func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// UpdateMetadata will update the Snapshot with provided information. To +// extract the updated Snapshot from the response, call the ExtractMetadata +// method on the UpdateMetadataResult. +func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { + b, err := opts.ToSnapshotUpdateMetadataMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/snapshots/results.go b/openstack/evs/v3/snapshots/results.go new file mode 100644 index 000000000..3c81a447a --- /dev/null +++ b/openstack/evs/v3/snapshots/results.go @@ -0,0 +1,139 @@ +package snapshots + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Snapshot contains all the information associated with a Cinder Snapshot. +type Snapshot struct { + // Unique identifier. + ID string `json:"id"` + + // Date created. + CreatedAt time.Time `json:"-"` + + // Date updated. + UpdatedAt time.Time `json:"-"` + + // Display name. + Name string `json:"name"` + + // Display description. + Description string `json:"description"` + + // ID of the Volume from which this Snapshot was created. + VolumeID string `json:"volume_id"` + + // Currect status of the Snapshot. + Status string `json:"status"` + + // Size of the Snapshot, in GB. + Size int `json:"size"` + + // User-defined key-value pairs. + Metadata map[string]string `json:"metadata"` +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} + +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + +// SnapshotPage is a pagination.Pager that is returned from a call to the List function. +type SnapshotPage struct { + pagination.LinkedPageBase +} + +// UnmarshalJSON converts our JSON API response into our snapshot struct +func (r *Snapshot) UnmarshalJSON(b []byte) error { + type tmp Snapshot + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Snapshot(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} + +// IsEmpty returns true if a SnapshotPage contains no Snapshots. +func (r SnapshotPage) IsEmpty() (bool, error) { + volumes, err := ExtractSnapshots(r) + return len(volumes) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r SnapshotPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"snapshots_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. +func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { + var s struct { + Snapshots []Snapshot `json:"snapshots"` + } + err := (r.(SnapshotPage)).ExtractInto(&s) + return s.Snapshots, err +} + +// UpdateMetadataResult contains the response body and error from an UpdateMetadata request. +type UpdateMetadataResult struct { + commonResult +} + +// ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. +func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { + if r.Err != nil { + return nil, r.Err + } + m := r.Body.(map[string]interface{})["metadata"] + return m.(map[string]interface{}), nil +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Snapshot object out of the commonResult object. +func (r commonResult) Extract() (*Snapshot, error) { + var s struct { + Snapshot *Snapshot `json:"snapshot"` + } + err := r.ExtractInto(&s) + return s.Snapshot, err +} diff --git a/openstack/evs/v3/snapshots/testing/doc.go b/openstack/evs/v3/snapshots/testing/doc.go new file mode 100644 index 000000000..89a5d0d3e --- /dev/null +++ b/openstack/evs/v3/snapshots/testing/doc.go @@ -0,0 +1,2 @@ +// Package testing for snapshots_v3 +package testing diff --git a/openstack/evs/v3/snapshots/testing/fixtures.go b/openstack/evs/v3/snapshots/testing/fixtures.go new file mode 100644 index 000000000..3ae3bb86d --- /dev/null +++ b/openstack/evs/v3/snapshots/testing/fixtures.go @@ -0,0 +1,178 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +// MockListResponse provides mock responce for list snapshot API call +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ` + { + "snapshots": [ + { + "id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "name": "snapshot-001", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "description": "Daily Backup", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + }, + { + "id": "96c3bda7-c82a-4f50-be73-ca7621794835", + "name": "snapshot-002", + "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", + "description": "Weekly Backup", + "status": "available", + "size": 25, + "created_at": "2017-05-30T03:35:03.000000" + } + ], + "snapshots_links": [ + { + "href": "%s/snapshots?marker=1", + "rel": "next" + }] + } + `, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"snapshots": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +// MockGetResponse provides mock responce for get snapshot API call +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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, ` +{ + "snapshot": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "snapshot-001", + "description": "Daily backup", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + } +} + `) + }) +} + +// MockCreateResponse provides mock responce for create snapshot API call +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots", 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, ` +{ + "snapshot": { + "volume_id": "1234", + "name": "snapshot-001" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, ` +{ + "snapshot": { + "volume_id": "1234", + "name": "snapshot-001", + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "description": "Daily backup", + "volume_id": "1234", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + } +} + `) + }) +} + +// MockUpdateMetadataResponse provides mock responce for update metadata snapshot API call +func MockUpdateMetadataResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots/123/metadata", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestJSONRequest(t, r, ` + { + "metadata": { + "key": "v1" + } + } + `) + + fmt.Fprintf(w, ` + { + "metadata": { + "key": "v1" + } + } + `) + }) +} + +// MockDeleteResponse provides mock responce for delete snapshot API call +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) +} + +// MockUpdateResponse provides mock responce for update snapshot API call +func MockUpdateResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` +{ + "snapshot": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "snapshot-002", + "description": "Daily backup 002", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000", + "updated_at": "2017-05-30T03:35:03.000000" + } +} + `) + }) +} diff --git a/openstack/evs/v3/snapshots/testing/requests_test.go b/openstack/evs/v3/snapshots/testing/requests_test.go new file mode 100644 index 000000000..a787928ba --- /dev/null +++ b/openstack/evs/v3/snapshots/testing/requests_test.go @@ -0,0 +1,131 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + count := 0 + + snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := snapshots.ExtractSnapshots(page) + if err != nil { + t.Errorf("Failed to extract snapshots: %v", err) + return false, err + } + + expected := []snapshots.Snapshot{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "snapshot-001", + VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + Status: "available", + Size: 30, + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), + Description: "Daily Backup", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "snapshot-002", + VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", + Status: "available", + Size: 25, + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), + Description: "Weekly Backup", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, v.Name, "snapshot-001") + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} + n, err := snapshots.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.VolumeID, "1234") + th.AssertEquals(t, n.Name, "snapshot-001") + th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestUpdateMetadata(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateMetadataResponse(t) + + expected := map[string]interface{}{"key": "v1"} + + options := &snapshots.UpdateMetadataOpts{ + Metadata: map[string]interface{}{ + "key": "v1", + }, + } + + actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, actual, expected) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateResponse(t) + + var name = "snapshot-002" + var description = "Daily backup 002" + options := snapshots.UpdateOpts{Name: &name, Description: &description} + v, err := snapshots.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + th.AssertNoErr(t, err) + th.CheckEquals(t, "snapshot-002", v.Name) + th.CheckEquals(t, "Daily backup 002", v.Description) +} diff --git a/openstack/evs/v3/snapshots/urls.go b/openstack/evs/v3/snapshots/urls.go new file mode 100644 index 000000000..d0bcbfb98 --- /dev/null +++ b/openstack/evs/v3/snapshots/urls.go @@ -0,0 +1,31 @@ +package snapshots + +import "github.com/gophercloud/gophercloud" + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("snapshots") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return deleteURL(c, id) +} + +func listURL(c *gophercloud.ServiceClient) string { + return createURL(c) +} + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return deleteURL(c, id) +} + +func metadataURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id, "metadata") +} + +func updateMetadataURL(c *gophercloud.ServiceClient, id string) string { + return metadataURL(c, id) +} diff --git a/openstack/evs/v3/snapshots/util.go b/openstack/evs/v3/snapshots/util.go new file mode 100644 index 000000000..40fbb827b --- /dev/null +++ b/openstack/evs/v3/snapshots/util.go @@ -0,0 +1,22 @@ +package snapshots + +import ( + "github.com/gophercloud/gophercloud" +) + +// WaitForStatus will continually poll the resource, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { + return gophercloud.WaitFor(secs, func() (bool, error) { + current, err := Get(c, id).Extract() + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} diff --git a/openstack/evs/v3/volumes/doc.go b/openstack/evs/v3/volumes/doc.go new file mode 100644 index 000000000..0b834852d --- /dev/null +++ b/openstack/evs/v3/volumes/doc.go @@ -0,0 +1,23 @@ +/* +Package volumes provides information and interaction with volumes in the +OpenStack Block Storage service. A volume is a detachable block storage +device, akin to a USB hard drive. It can only be attached to one instance at +a time. + +Example to create a Volume from a Backup + + backupID := "20c792f0-bb03-434f-b653-06ef238e337e" + options := volumes.CreateOpts{ + Name: "vol-001", + BackupID: &backupID, + } + + client.Microversion = "3.47" + volume, err := volumes.Create(client, options).Extract() + if err != nil { + panic(err) + } + + fmt.Println(volume) +*/ +package volumes diff --git a/openstack/evs/v3/volumes/requests.go b/openstack/evs/v3/volumes/requests.go index 41565d6f1..f6063c595 100644 --- a/openstack/evs/v3/volumes/requests.go +++ b/openstack/evs/v3/volumes/requests.go @@ -1,7 +1,8 @@ package volumes import ( - "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the @@ -14,58 +15,194 @@ type CreateOptsBuilder interface { // the volumes.Create function. For more information about these parameters, // see the Volume object. type CreateOpts struct { + // The size of the volume, in GB + Size int `json:"size,omitempty"` // The availability zone - AvailabilityZone string `json:"availability_zone" required:"true"` - // The associated volume type - VolumeType string `json:"volume_type" required:"true"` - // The volume name - Name string `json:"name,omitempty"` + AvailabilityZone string `json:"availability_zone,omitempty"` + // ConsistencyGroupID is the ID of a consistency group + ConsistencyGroupID string `json:"consistencygroup_id,omitempty"` // The volume description Description string `json:"description,omitempty"` - // The size of the volume, in GB - Size int `json:"size,omitempty"` - // The number to be created in a batch - Count int `json:"count,omitempty"` - // The backup_id - BackupID string `json:"backup_id,omitempty"` + // One or more metadata key and value pairs to associate with the volume + Metadata map[string]string `json:"metadata,omitempty"` + // The volume name + Name string `json:"name,omitempty"` // the ID of the existing volume snapshot SnapshotID string `json:"snapshot_id,omitempty"` - // the ID of the image in IMS - ImageRef string `json:"imageRef,omitempty"` - // This field is no longer used. Use multiattach. - Shareable string `json:"shareable,omitempty"` - // Shared disk + // SourceReplica is a UUID of an existing volume to replicate with + SourceReplica string `json:"source_replica,omitempty"` + // the ID of the existing volume + SourceVolID string `json:"source_volid,omitempty"` + // The ID of the image from which you want to create the volume. + // Required to create a bootable volume. + ImageID string `json:"imageRef,omitempty"` + // Specifies the backup ID, from which you want to create the volume. + // Create a volume from a backup is supported since 3.47 microversion + BackupID string `json:"backup_id,omitempty"` + // The associated volume type + VolumeType string `json:"volume_type,omitempty"` + // Multiattach denotes if the volume is multi-attach capable. Multiattach bool `json:"multiattach,omitempty"` - // One or more metadata key and value pairs to associate with the volume - Metadata map[string]string `json:"metadata,omitempty"` - // One or more tag key and value pairs to associate with the volume - Tags map[string]string `json:"tags,omitempty"` - // the enterprise project id - EnterpriseProjectID string `json:"enterprise_project_id,omitempty"` } // ToVolumeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "volume") + return gophercloud.BuildRequestBody(opts, "volume") } -// Create will create a new Volume based on the values in CreateOpts. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r JobResult) { +// Create will create a new Volume based on the values in CreateOpts. To extract +// the Volume object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeCreateMap() if err != nil { r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteOptsBuilder allows extensions to add additional parameters to the +// Delete request. +type DeleteOptsBuilder interface { + ToVolumeDeleteQuery() (string, error) +} + +// DeleteOpts contains options for deleting a Volume. This object is passed to +// the volumes.Delete function. +type DeleteOpts struct { + // Delete all snapshots of this volume as well. + Cascade bool `q:"cascade"` +} + +// ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string. +func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Delete will delete the existing Volume with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { + url := deleteURL(client, id) + if opts != nil { + query, err := opts.ToVolumeDeleteQuery() + if err != nil { + r.Err = err + return + } + url += query + } + resp, err := client.Delete(url, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToVolumeListQuery() (string, error) +} + +// ListOpts holds options for listing Volumes. It is passed to the volumes.List +// function. +type ListOpts struct { + // AllTenants will retrieve volumes of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Metadata will filter results based on specified metadata. + Metadata map[string]string `q:"metadata"` + + // Name will filter by the specified volume name. + Name string `q:"name"` + + // Status will filter by the specified status. + Status string `q:"status"` + + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required for this. + TenantID string `q:"project_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToVolumeListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToVolumeListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Volumes 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.ToVolumeListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return VolumePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToVolumeUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Volume. This object is passed +// to the volumes.Update function. For more information about the parameters, see +// the Volume object. +type UpdateOpts struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` +} + +// ToVolumeUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "volume") +} + +// Update will update the Volume with provided information. To extract the updated +// Volume from the response, call the Extract method on the UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToVolumeUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/evs/v3/volumes/results.go b/openstack/evs/v3/volumes/results.go index edc457072..6f46685b6 100644 --- a/openstack/evs/v3/volumes/results.go +++ b/openstack/evs/v3/volumes/results.go @@ -4,9 +4,11 @@ import ( "encoding/json" "time" - "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) +// Attachment represents a Volume Attachment record type Attachment struct { AttachedAt time.Time `json:"-"` AttachmentID string `json:"attachment_id"` @@ -17,11 +19,12 @@ type Attachment struct { VolumeID string `json:"volume_id"` } +// UnmarshalJSON is our unmarshalling helper func (r *Attachment) UnmarshalJSON(b []byte) error { type tmp Attachment var s struct { tmp - AttachedAt golangsdk.JSONRFC3339MilliNoZ `json:"attached_at"` + AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -34,7 +37,7 @@ func (r *Attachment) UnmarshalJSON(b []byte) error { return err } -// Volume contains all the information associated with a Volume. +// Volume contains all the information associated with an OpenStack Volume. type Volume struct { // Unique identifier for the volume. ID string `json:"id"` @@ -60,12 +63,11 @@ type Volume struct { SnapshotID string `json:"snapshot_id"` // The ID of another block storage volume from which the current volume was created SourceVolID string `json:"source_volid"` - // The ID of the back that can be used to create an EVS disk - BackupID string `json:"backup_id"` - // Arbitrary key-value pairs defined by the metadata field table. - Metadata map[string]string `json:"metadata"` + // The backup ID, from which the volume was restored + // This field is supported since 3.47 microversion + BackupID *string `json:"backup_id"` // Arbitrary key-value pairs defined by the user. - Tags map[string]string `json:"tags"` + Metadata map[string]string `json:"metadata"` // UserID is the id of the user who created the volume. UserID string `json:"user_id"` // Indicates whether this is a bootable volume. @@ -78,18 +80,17 @@ type Volume struct { ConsistencyGroupID string `json:"consistencygroup_id"` // Multiattach denotes if the volume is multi-attach capable. Multiattach bool `json:"multiattach"` - // wwn of the volume. - WWN string `json:"wwn"` - // enterprise project ID bound to the volume - EnterpriseProjectID string `json:"enterprise_project_id"` + // Image metadata entries, only included for volumes that were created from an image, or from a snapshot of a volume originally created from an image. + VolumeImageMetadata map[string]string `json:"volume_image_metadata"` } +// UnmarshalJSON another unmarshalling function func (r *Volume) UnmarshalJSON(b []byte) error { type tmp Volume var s struct { tmp - CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -103,8 +104,37 @@ func (r *Volume) UnmarshalJSON(b []byte) error { return err } +// VolumePage is a pagination.pager that is returned from a call to the List function. +type VolumePage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a ListResult contains no Volumes. +func (r VolumePage) IsEmpty() (bool, error) { + volumes, err := ExtractVolumes(r) + return len(volumes) == 0, err +} + +func (page VolumePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"volumes_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. +func ExtractVolumes(r pagination.Page) ([]Volume, error) { + var s []Volume + err := ExtractVolumesInto(r, &s) + return s, err +} + type commonResult struct { - golangsdk.Result + gophercloud.Result } // Extract will get the Volume object out of the commonResult object. @@ -119,7 +149,27 @@ func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "volume") } +// ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes +func ExtractVolumesInto(r pagination.Page, v interface{}) error { + return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes") +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } + +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/evs/v3/volumes/results_job.go b/openstack/evs/v3/volumes/results_job.go deleted file mode 100644 index 2b1696ee2..000000000 --- a/openstack/evs/v3/volumes/results_job.go +++ /dev/null @@ -1,94 +0,0 @@ -package volumes - -import ( - "fmt" - "strings" - - "github.com/opentelekomcloud/gophertelekomcloud" -) - -type JobResponse struct { - JobID string `json:"job_id"` -} - -type JobStatus struct { - Status string `json:"status"` - Entities JobEntity `json:"entities"` - JobID string `json:"job_id"` - JobType string `json:"job_type"` - BeginTime string `json:"begin_time"` - EndTime string `json:"end_time"` - ErrorCode string `json:"error_code"` - FailReason string `json:"fail_reason"` - Message string `json:"message"` - Code string `json:"code"` - SubJobs []JobStatus `json:"sub_jobs"` -} - -type JobEntity struct { - VolumeID string `json:"volume_id"` -} - -type JobResult struct { - golangsdk.Result -} - -func (r JobResult) ExtractJobResponse() (*JobResponse, error) { - job := new(JobResponse) - err := r.ExtractInto(job) - return job, err -} - -func (r JobResult) ExtractJobStatus() (*JobStatus, error) { - job := new(JobStatus) - err := r.ExtractInto(job) - return job, err -} - -func WaitForJobSuccess(client *golangsdk.ServiceClient, secs int, jobID string) error { - - jobClient := *client - jobClient.Endpoint = strings.Replace(jobClient.Endpoint, "v3", "v1", 1) - jobClient.ResourceBase = jobClient.Endpoint - return golangsdk.WaitFor(secs, func() (bool, error) { - job := new(JobStatus) - _, err := jobClient.Get(jobClient.ServiceURL("jobs", jobID), &job, nil) - if err != nil { - return false, err - } - - if job.Status == "SUCCESS" { - return true, nil - } - if job.Status == "FAIL" { - err = fmt.Errorf("Job failed with code %s: %s.\n", job.ErrorCode, job.FailReason) - return false, err - } - - return false, nil - }) -} - -func GetJobEntity(client *golangsdk.ServiceClient, jobId string, label string) (interface{}, error) { - - if label != "volume_id" { - return nil, fmt.Errorf("Unsupported label %s in GetJobEntity.", label) - } - - jobClient := *client - jobClient.Endpoint = strings.Replace(jobClient.Endpoint, "v3", "v1", 1) - jobClient.ResourceBase = jobClient.Endpoint - job := new(JobStatus) - _, err := jobClient.Get(jobClient.ServiceURL("jobs", jobId), &job, nil) - if err != nil { - return nil, err - } - - if job.Status == "SUCCESS" { - if e := job.Entities.VolumeID; e != "" { - return e, nil - } - } - - return nil, fmt.Errorf("Unexpected conversion error in GetJobEntity.") -} diff --git a/openstack/evs/v3/volumes/testing/doc.go b/openstack/evs/v3/volumes/testing/doc.go new file mode 100644 index 000000000..a2b24b7c1 --- /dev/null +++ b/openstack/evs/v3/volumes/testing/doc.go @@ -0,0 +1,2 @@ +// volumes_v3 +package testing diff --git a/openstack/evs/v3/volumes/testing/fixtures.go b/openstack/evs/v3/volumes/testing/fixtures.go new file mode 100644 index 000000000..339cb30c9 --- /dev/null +++ b/openstack/evs/v3/volumes/testing/fixtures.go @@ -0,0 +1,265 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/detail", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ` + { + "volumes": [ + { + "volume_type": "lvmdriver-1", + "created_at": "2015-09-17T03:35:03.000000", + "bootable": "false", + "name": "vol-001", + "os-vol-mig-status-attr:name_id": null, + "consistencygroup_id": null, + "source_volid": null, + "os-volume-replication:driver_data": null, + "multiattach": false, + "snapshot_id": null, + "replication_status": "disabled", + "os-volume-replication:extended_status": null, + "encrypted": false, + "os-vol-host-attr:host": "host-001", + "availability_zone": "nova", + "attachments": [{ + "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", + "attached_at": "2016-08-06T14:48:20.000000", + "host_name": "foobar", + "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + "device": "/dev/vdc", + "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" + }], + "id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "size": 75, + "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", + "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", + "os-vol-mig-status-attr:migstat": null, + "metadata": {"foo": "bar"}, + "status": "available", + "description": null + }, + { + "volume_type": "lvmdriver-1", + "created_at": "2015-09-17T03:32:29.000000", + "bootable": "false", + "name": "vol-002", + "os-vol-mig-status-attr:name_id": null, + "consistencygroup_id": null, + "source_volid": null, + "os-volume-replication:driver_data": null, + "multiattach": false, + "snapshot_id": null, + "replication_status": "disabled", + "os-volume-replication:extended_status": null, + "encrypted": false, + "os-vol-host-attr:host": null, + "availability_zone": "nova", + "attachments": [], + "id": "96c3bda7-c82a-4f50-be73-ca7621794835", + "size": 75, + "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", + "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", + "os-vol-mig-status-attr:migstat": null, + "metadata": {}, + "status": "available", + "description": null + } + ], + "volumes_links": [ + { + "href": "%s/volumes/detail?marker=1", + "rel": "next" + }] +} + `, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"volumes": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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": { + "volume_type": "lvmdriver-1", + "created_at": "2015-09-17T03:32:29.000000", + "bootable": "false", + "name": "vol-001", + "os-vol-mig-status-attr:name_id": null, + "consistencygroup_id": null, + "source_volid": null, + "os-volume-replication:driver_data": null, + "multiattach": false, + "snapshot_id": null, + "replication_status": "disabled", + "os-volume-replication:extended_status": null, + "encrypted": false, + "availability_zone": "nova", + "attachments": [{ + "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", + "attached_at": "2016-08-06T14:48:20.000000", + "host_name": "foobar", + "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + "device": "/dev/vdc", + "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" + }], + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "size": 75, + "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", + "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", + "os-vol-mig-status-attr:migstat": null, + "metadata": {}, + "status": "available", + "volume_image_metadata": { + "container_format": "bare", + "image_name": "centos" + }, + "description": null + } +} + `) + }) +} + +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes", 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, ` +{ + "volume": { + "name": "vol-001", + "size": 75 + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, ` +{ + "volume": { + "size": 75, + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "metadata": {}, + "created_at": "2015-09-17T03:32:29.044216", + "encrypted": false, + "bootable": "false", + "availability_zone": "nova", + "attachments": [], + "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", + "status": "creating", + "description": null, + "volume_type": "lvmdriver-1", + "name": "vol-001", + "replication_status": "disabled", + "consistencygroup_id": null, + "source_volid": null, + "snapshot_id": null, + "multiattach": false + } +} + `) + }) +} + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} + +func MockUpdateResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` +{ + "volume": { + "name": "vol-002" + } +} + `) + }) +} + +func MockCreateVolumeFromBackupResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes", 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, ` +{ + "volume": { + "name": "vol-001", + "backup_id": "20c792f0-bb03-434f-b653-06ef238e337e" + } +} +`) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, ` +{ + "volume": { + "size": 30, + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "metadata": {}, + "created_at": "2015-09-17T03:32:29.044216", + "encrypted": false, + "bootable": "false", + "availability_zone": "nova", + "attachments": [], + "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", + "status": "creating", + "description": null, + "volume_type": "lvmdriver-1", + "name": "vol-001", + "replication_status": "disabled", + "consistencygroup_id": null, + "source_volid": null, + "snapshot_id": null, + "backup_id": "20c792f0-bb03-434f-b653-06ef238e337e", + "multiattach": false + } +}`) + }) +} diff --git a/openstack/evs/v3/volumes/testing/requests_test.go b/openstack/evs/v3/volumes/testing/requests_test.go new file mode 100644 index 000000000..a4ecdae23 --- /dev/null +++ b/openstack/evs/v3/volumes/testing/requests_test.go @@ -0,0 +1,282 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumehost" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetenants" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListWithExtensions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + count := 0 + + volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := volumes.ExtractVolumes(page) + if err != nil { + t.Errorf("Failed to extract volumes: %v", err) + return false, err + } + + expected := []volumes.Volume{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "vol-001", + Attachments: []volumes.Attachment{{ + ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", + AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", + AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), + HostName: "foobar", + VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + Device: "/dev/vdc", + ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + }}, + AvailabilityZone: "nova", + Bootable: "false", + ConsistencyGroupID: "", + CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), + Description: "", + Encrypted: false, + Metadata: map[string]string{"foo": "bar"}, + Multiattach: false, + //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + //ReplicationDriverData: "", + //ReplicationExtendedStatus: "", + ReplicationStatus: "disabled", + Size: 75, + SnapshotID: "", + SourceVolID: "", + Status: "available", + UserID: "ff1ce52c03ab433aaba9108c2e3ef541", + VolumeType: "lvmdriver-1", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "vol-002", + Attachments: []volumes.Attachment{}, + AvailabilityZone: "nova", + Bootable: "false", + ConsistencyGroupID: "", + CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), + Description: "", + Encrypted: false, + Metadata: map[string]string{}, + Multiattach: false, + //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + //ReplicationDriverData: "", + //ReplicationExtendedStatus: "", + ReplicationStatus: "disabled", + Size: 75, + SnapshotID: "", + SourceVolID: "", + Status: "available", + UserID: "ff1ce52c03ab433aaba9108c2e3ef541", + VolumeType: "lvmdriver-1", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestListAllWithExtensions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + type VolumeWithExt struct { + volumes.Volume + volumetenants.VolumeTenantExt + volumehost.VolumeHostExt + } + + allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + var actual []VolumeWithExt + err = volumes.ExtractVolumesInto(allPages, &actual) + th.AssertNoErr(t, err) + th.AssertEquals(t, 2, len(actual)) + th.AssertEquals(t, "host-001", actual[0].Host) + th.AssertEquals(t, "", actual[1].Host) + th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", actual[0].TenantID) +} + +func TestListAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := volumes.ExtractVolumes(allPages) + th.AssertNoErr(t, err) + + expected := []volumes.Volume{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "vol-001", + Attachments: []volumes.Attachment{{ + ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", + AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", + AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), + HostName: "foobar", + VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + Device: "/dev/vdc", + ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + }}, + AvailabilityZone: "nova", + Bootable: "false", + ConsistencyGroupID: "", + CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), + Description: "", + Encrypted: false, + Metadata: map[string]string{"foo": "bar"}, + Multiattach: false, + //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + //ReplicationDriverData: "", + //ReplicationExtendedStatus: "", + ReplicationStatus: "disabled", + Size: 75, + SnapshotID: "", + SourceVolID: "", + Status: "available", + UserID: "ff1ce52c03ab433aaba9108c2e3ef541", + VolumeType: "lvmdriver-1", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "vol-002", + Attachments: []volumes.Attachment{}, + AvailabilityZone: "nova", + Bootable: "false", + ConsistencyGroupID: "", + CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), + Description: "", + Encrypted: false, + Metadata: map[string]string{}, + Multiattach: false, + //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + //ReplicationDriverData: "", + //ReplicationExtendedStatus: "", + ReplicationStatus: "disabled", + Size: 75, + SnapshotID: "", + SourceVolID: "", + Status: "available", + UserID: "ff1ce52c03ab433aaba9108c2e3ef541", + VolumeType: "lvmdriver-1", + }, + } + + th.CheckDeepEquals(t, expected, actual) + +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + v, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, v.Name, "vol-001") + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := &volumes.CreateOpts{Size: 75, Name: "vol-001"} + n, err := volumes.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Size, 75) + th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", volumes.DeleteOpts{}) + th.AssertNoErr(t, res.Err) +} + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateResponse(t) + + var name = "vol-002" + options := volumes.UpdateOpts{Name: &name} + v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + th.AssertNoErr(t, err) + th.CheckEquals(t, "vol-002", v.Name) +} + +func TestGetWithExtensions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + var s struct { + volumes.Volume + volumetenants.VolumeTenantExt + } + err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) + th.AssertNoErr(t, err) + th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", s.TenantID) + th.AssertEquals(t, "centos", s.Volume.VolumeImageMetadata["image_name"]) + + err = volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) + if err == nil { + t.Errorf("Expected error when providing non-pointer struct") + } +} + +func TestCreateFromBackup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateVolumeFromBackupResponse(t) + + options := volumes.CreateOpts{ + Name: "vol-001", + BackupID: "20c792f0-bb03-434f-b653-06ef238e337e", + } + + v, err := volumes.Create(client.ServiceClient(), options).Extract() + + th.AssertNoErr(t, err) + th.AssertEquals(t, v.Size, 30) + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertEquals(t, *v.BackupID, "20c792f0-bb03-434f-b653-06ef238e337e") +} diff --git a/openstack/evs/v3/volumes/urls.go b/openstack/evs/v3/volumes/urls.go index 88cd67f31..170724905 100644 --- a/openstack/evs/v3/volumes/urls.go +++ b/openstack/evs/v3/volumes/urls.go @@ -1,11 +1,23 @@ package volumes -import "github.com/opentelekomcloud/gophertelekomcloud" +import "github.com/gophercloud/gophercloud" -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("cloudvolumes") +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("volumes") } -func getURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("os-vendor-volumes", id) +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("volumes", "detail") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("volumes", id) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return deleteURL(c, id) +} + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return deleteURL(c, id) } diff --git a/openstack/evs/v3/volumes/util.go b/openstack/evs/v3/volumes/util.go new file mode 100644 index 000000000..e86c1b4b4 --- /dev/null +++ b/openstack/evs/v3/volumes/util.go @@ -0,0 +1,22 @@ +package volumes + +import ( + "github.com/gophercloud/gophercloud" +) + +// WaitForStatus will continually poll the resource, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { + return gophercloud.WaitFor(secs, func() (bool, error) { + current, err := Get(c, id).Extract() + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} diff --git a/openstack/evs/v3/volumetypes/doc.go b/openstack/evs/v3/volumetypes/doc.go new file mode 100644 index 000000000..55a2170bc --- /dev/null +++ b/openstack/evs/v3/volumetypes/doc.go @@ -0,0 +1,164 @@ +/* +Package volumetypes provides information and interaction with volume types in the +OpenStack Block Storage service. A volume type is a collection of specs used to +define the volume capabilities. + +Example to list Volume Types + + allPages, err := volumetypes.List(client, volumetypes.ListOpts{}).AllPages() + if err != nil{ + panic(err) + } + volumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) + if err != nil{ + panic(err) + } + for _,vt := range volumeTypes{ + fmt.Println(vt) + } + +Example to show a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + volumeType, err := volumetypes.Get(client, typeID).Extract() + if err != nil{ + panic(err) + } + fmt.Println(volumeType) + +Example to create a Volume Type + + volumeType, err := volumetypes.Create(client, volumetypes.CreateOpts{ + Name:"volume_type_001", + IsPublic:true, + Description:"description_001", + }).Extract() + if err != nil{ + panic(err) + } + fmt.Println(volumeType) + +Example to delete a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + err := volumetypes.Delete(client, typeID).ExtractErr() + if err != nil{ + panic(err) + } + +Example to update a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + volumetype, err = volumetypes.Update(client, typeID, volumetypes.UpdateOpts{ + Name: "volume_type_002", + Description:"description_002", + IsPublic:false, + }).Extract() + if err != nil{ + panic(err) + } + fmt.Println(volumetype) + +Example to Create Extra Specs for a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + + createOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "gpu", + } + createdExtraSpecs, err := volumetypes.CreateExtraSpecs(client, typeID, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", createdExtraSpecs) + +Example to Get Extra Specs for a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + + extraSpecs, err := volumetypes.ListExtraSpecs(client, typeID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", extraSpecs) + +Example to Get specific Extra Spec for a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + + extraSpec, err := volumetypes.GetExtraSpec(client, typeID, "capabilities").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", extraSpec) + +Example to Update Extra Specs for a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + + updateOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "capabilities-updated", + } + updatedExtraSpec, err := volumetypes.UpdateExtraSpec(client, typeID, updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", updatedExtraSpec) + +Example to Delete an Extra Spec for a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + err := volumetypes.DeleteExtraSpec(client, typeID, "capabilities").ExtractErr() + if err != nil { + panic(err) + } + +Example to List Volume Type Access + + typeID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + allPages, err := volumetypes.ListAccesses(client, typeID).AllPages() + if err != nil { + panic(err) + } + + allAccesses, err := volumetypes.ExtractAccesses(allPages) + if err != nil { + panic(err) + } + + for _, access := range allAccesses { + fmt.Printf("%+v", access) + } + +Example to Grant Access to a Volume Type + + typeID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + accessOpts := volumetypes.AddAccessOpts{ + Project: "15153a0979884b59b0592248ef947921", + } + + err := volumetypes.AddAccess(client, typeID, accessOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example to Remove/Revoke Access to a Volume Type + + typeID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + accessOpts := volumetypes.RemoveAccessOpts{ + Project: "15153a0979884b59b0592248ef947921", + } + + err := volumetypes.RemoveAccess(client, typeID, accessOpts).ExtractErr() + if err != nil { + panic(err) + } +*/ +package volumetypes diff --git a/openstack/evs/v3/volumetypes/requests.go b/openstack/evs/v3/volumetypes/requests.go new file mode 100644 index 000000000..5b272bf05 --- /dev/null +++ b/openstack/evs/v3/volumetypes/requests.go @@ -0,0 +1,306 @@ +package volumetypes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToVolumeTypeCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for creating a Volume Type. This object is passed to +// the volumetypes.Create function. For more information about these parameters, +// see the Volume Type object. +type CreateOpts struct { + // The name of the volume type + Name string `json:"name" required:"true"` + // The volume type description + Description string `json:"description,omitempty"` + // the ID of the existing volume snapshot + IsPublic *bool `json:"os-volume-type-access:is_public,omitempty"` + // Extra spec key-value pairs defined by the user. + ExtraSpecs map[string]string `json:"extra_specs,omitempty"` +} + +// ToVolumeTypeCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "volume_type") +} + +// Create will create a new Volume Type based on the values in CreateOpts. To extract +// the Volume Type object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToVolumeTypeCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete will delete the existing Volume Type with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get retrieves the Volume Type with the provided ID. To extract the Volume Type object +// from the response, call the Extract method on the GetResult. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToVolumeTypeListQuery() (string, error) +} + +// ListOpts holds options for listing Volume Types. It is passed to the volumetypes.List +// function. +type ListOpts struct { + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + // Requests a page size of items. + Limit int `q:"limit"` + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToVolumeTypeListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToVolumeTypeListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Volume types. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + + if opts != nil { + query, err := opts.ToVolumeTypeListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return VolumeTypePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToVolumeTypeUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Volume Type. This object is passed +// to the volumetypes.Update function. For more information about the parameters, see +// the Volume Type object. +type UpdateOpts struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + IsPublic *bool `json:"is_public,omitempty"` +} + +// ToVolumeTypeUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "volume_type") +} + +// Update will update the Volume Type with provided information. To extract the updated +// Volume Type from the response, call the Extract method on the UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToVolumeTypeUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListExtraSpecs requests all the extra-specs for the given volume type ID. +func ListExtraSpecs(client *gophercloud.ServiceClient, volumeTypeID string) (r ListExtraSpecsResult) { + resp, err := client.Get(extraSpecsListURL(client, volumeTypeID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetExtraSpec requests an extra-spec specified by key for the given volume type ID +func GetExtraSpec(client *gophercloud.ServiceClient, volumeTypeID string, key string) (r GetExtraSpecResult) { + resp, err := client.Get(extraSpecsGetURL(client, volumeTypeID, key), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the +// CreateExtraSpecs requests. +type CreateExtraSpecsOptsBuilder interface { + ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) +} + +// ExtraSpecsOpts is a map that contains key-value pairs. +type ExtraSpecsOpts map[string]string + +// ToVolumeTypeExtraSpecsCreateMap assembles a body for a Create request based on +// the contents of ExtraSpecsOpts. +func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) { + return map[string]interface{}{"extra_specs": opts}, nil +} + +// CreateExtraSpecs will create or update the extra-specs key-value pairs for +// the specified volume type. +func CreateExtraSpecs(client *gophercloud.ServiceClient, volumeTypeID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { + b, err := opts.ToVolumeTypeExtraSpecsCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(extraSpecsCreateURL(client, volumeTypeID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateExtraSpecOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateExtraSpecOptsBuilder interface { + ToVolumeTypeExtraSpecUpdateMap() (map[string]string, string, error) +} + +// ToVolumeTypeExtraSpecUpdateMap assembles a body for an Update request based on +// the contents of a ExtraSpecOpts. +func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecUpdateMap() (map[string]string, string, error) { + if len(opts) != 1 { + err := gophercloud.ErrInvalidInput{} + err.Argument = "volumetypes.ExtraSpecOpts" + err.Info = "Must have one and only one key-value pair" + return nil, "", err + } + + var key string + for k := range opts { + key = k + } + + return opts, key, nil +} + +// UpdateExtraSpec will updates the value of the specified volume type's extra spec +// for the key in opts. +func UpdateExtraSpec(client *gophercloud.ServiceClient, volumeTypeID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { + b, key, err := opts.ToVolumeTypeExtraSpecUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(extraSpecUpdateURL(client, volumeTypeID, key), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteExtraSpec will delete the key-value pair with the given key for the given +// volume type ID. +func DeleteExtraSpec(client *gophercloud.ServiceClient, volumeTypeID, key string) (r DeleteExtraSpecResult) { + resp, err := client.Delete(extraSpecDeleteURL(client, volumeTypeID, key), &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListAccesses retrieves the tenants which have access to a volume type. +func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager { + url := accessURL(client, id) + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessPage{pagination.SinglePageBase(r)} + }) +} + +// AddAccessOptsBuilder allows extensions to add additional parameters to the +// AddAccess requests. +type AddAccessOptsBuilder interface { + ToVolumeTypeAddAccessMap() (map[string]interface{}, error) +} + +// AddAccessOpts represents options for adding access to a volume type. +type AddAccessOpts struct { + // Project is the project/tenant ID to grant access. + Project string `json:"project"` +} + +// ToVolumeTypeAddAccessMap constructs a request body from AddAccessOpts. +func (opts AddAccessOpts) ToVolumeTypeAddAccessMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "addProjectAccess") +} + +// AddAccess grants a tenant/project access to a volume type. +func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { + b, err := opts.ToVolumeTypeAddAccessMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(accessActionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RemoveAccessOptsBuilder allows extensions to add additional parameters to the +// RemoveAccess requests. +type RemoveAccessOptsBuilder interface { + ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) +} + +// RemoveAccessOpts represents options for removing access to a volume type. +type RemoveAccessOpts struct { + // Project is the project/tenant ID to remove access. + Project string `json:"project"` +} + +// ToVolumeTypeRemoveAccessMap constructs a request body from RemoveAccessOpts. +func (opts RemoveAccessOpts) ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "removeProjectAccess") +} + +// RemoveAccess removes/revokes a tenant/project access to a volume type. +func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { + b, err := opts.ToVolumeTypeRemoveAccessMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(accessActionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/results.go b/openstack/evs/v3/volumetypes/results.go new file mode 100644 index 000000000..72c696ef1 --- /dev/null +++ b/openstack/evs/v3/volumetypes/results.go @@ -0,0 +1,194 @@ +package volumetypes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// VolumeType contains all the information associated with an OpenStack Volume Type. +type VolumeType struct { + // Unique identifier for the volume type. + ID string `json:"id"` + // Human-readable display name for the volume type. + Name string `json:"name"` + // Human-readable description for the volume type. + Description string `json:"description"` + // Arbitrary key-value pairs defined by the user. + ExtraSpecs map[string]string `json:"extra_specs"` + // Whether the volume type is publicly visible. + IsPublic bool `json:"is_public"` + // Qos Spec ID + QosSpecID string `json:"qos_specs_id"` + // Volume Type access public attribute + PublicAccess bool `json:"os-volume-type-access:is_public"` +} + +// VolumeTypePage is a pagination.pager that is returned from a call to the List function. +type VolumeTypePage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a ListResult contains no Volume Types. +func (r VolumeTypePage) IsEmpty() (bool, error) { + volumetypes, err := ExtractVolumeTypes(r) + return len(volumetypes) == 0, err +} + +func (page VolumeTypePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"volume_type_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractVolumeTypes extracts and returns Volumes. It is used while iterating over a volumetypes.List call. +func ExtractVolumeTypes(r pagination.Page) ([]VolumeType, error) { + var s []VolumeType + err := ExtractVolumeTypesInto(r, &s) + return s, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Volume Type object out of the commonResult object. +func (r commonResult) Extract() (*VolumeType, error) { + var s VolumeType + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto converts our response data into a volume type struct +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "volume_type") +} + +// ExtractVolumeTypesInto similar to ExtractInto but operates on a `list` of volume types +func ExtractVolumeTypesInto(r pagination.Page, v interface{}) error { + return r.(VolumeTypePage).Result.ExtractIntoSlicePtr(v, "volume_types") +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} + +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + +// extraSpecsResult contains the result of a call for (potentially) multiple +// key-value pairs. Call its Extract method to interpret it as a +// map[string]interface. +type extraSpecsResult struct { + gophercloud.Result +} + +// ListExtraSpecsResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. +type ListExtraSpecsResult struct { + extraSpecsResult +} + +// CreateExtraSpecsResult contains the result of a Create operation. Call its +// Extract method to interpret it as a map[string]interface. +type CreateExtraSpecsResult struct { + extraSpecsResult +} + +// Extract interprets any extraSpecsResult as ExtraSpecs, if possible. +func (r extraSpecsResult) Extract() (map[string]string, error) { + var s struct { + ExtraSpecs map[string]string `json:"extra_specs"` + } + err := r.ExtractInto(&s) + return s.ExtraSpecs, err +} + +// extraSpecResult contains the result of a call for individual a single +// key-value pair. +type extraSpecResult struct { + gophercloud.Result +} + +// GetExtraSpecResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. +type GetExtraSpecResult struct { + extraSpecResult +} + +// UpdateExtraSpecResult contains the result of an Update operation. Call its +// Extract method to interpret it as a map[string]interface. +type UpdateExtraSpecResult struct { + extraSpecResult +} + +// DeleteExtraSpecResult contains the result of a Delete operation. Call its +// ExtractErr method to determine if the call succeeded or failed. +type DeleteExtraSpecResult struct { + gophercloud.ErrResult +} + +// Extract interprets any extraSpecResult as an ExtraSpec, if possible. +func (r extraSpecResult) Extract() (map[string]string, error) { + var s map[string]string + err := r.ExtractInto(&s) + return s, err +} + +// VolumeTypeAccess represents an ACL of project access to a specific Volume Type. +type VolumeTypeAccess struct { + // VolumeTypeID is the unique ID of the volume type. + VolumeTypeID string `json:"volume_type_id"` + + // ProjectID is the unique ID of the project. + ProjectID string `json:"project_id"` +} + +// AccessPage contains a single page of all VolumeTypeAccess entries for a volume type. +type AccessPage struct { + pagination.SinglePageBase +} + +// IsEmpty indicates whether an AccessPage is empty. +func (page AccessPage) IsEmpty() (bool, error) { + v, err := ExtractAccesses(page) + return len(v) == 0, err +} + +// ExtractAccesses interprets a page of results as a slice of VolumeTypeAccess. +func ExtractAccesses(r pagination.Page) ([]VolumeTypeAccess, error) { + var s struct { + VolumeTypeAccesses []VolumeTypeAccess `json:"volume_type_access"` + } + err := (r.(AccessPage)).ExtractInto(&s) + return s.VolumeTypeAccesses, err +} + +// AddAccessResult is the response from a AddAccess request. Call its +// ExtractErr method to determine if the request succeeded or failed. +type AddAccessResult struct { + gophercloud.ErrResult +} + +// RemoveAccessResult is the response from a RemoveAccess request. Call its +// ExtractErr method to determine if the request succeeded or failed. +type RemoveAccessResult struct { + gophercloud.ErrResult +} diff --git a/openstack/evs/v3/volumetypes/testing/doc.go b/openstack/evs/v3/volumetypes/testing/doc.go new file mode 100644 index 000000000..3fd720a67 --- /dev/null +++ b/openstack/evs/v3/volumetypes/testing/doc.go @@ -0,0 +1,2 @@ +// volume_types +package testing diff --git a/openstack/evs/v3/volumetypes/testing/fixtures.go b/openstack/evs/v3/volumetypes/testing/fixtures.go new file mode 100644 index 000000000..eb617f19e --- /dev/null +++ b/openstack/evs/v3/volumetypes/testing/fixtures.go @@ -0,0 +1,260 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ` +{ + "volume_types": [ + { + "name": "SSD", + "qos_specs_id": null, + "os-volume-type-access:is_public": true, + "extra_specs": { + "volume_backend_name": "lvmdriver-1" + }, + "is_public": true, + "id": "6685584b-1eac-4da6-b5c3-555430cf68ff", + "description": null + }, + { + "name": "SATA", + "qos_specs_id": null, + "os-volume-type-access:is_public": true, + "extra_specs": { + "volume_backend_name": "lvmdriver-1" + }, + "is_public": true, + "id": "8eb69a46-df97-4e41-9586-9a40a7533803", + "description": null + } + ], + "volume_type_links": [ + { + "href": "%s/types?marker=1", + "rel": "next" + } + ] +} + `, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"volume_types": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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_type": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "vol-type-001", + "os-volume-type-access:is_public": true, + "qos_specs_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "description": "volume type 001", + "is_public": true, + "extra_specs": { + "capabilities": "gpu" + } + } +} +`) + }) +} + +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, ` +{ + "volume_type": { + "name": "test_type", + "os-volume-type-access:is_public": true, + "description": "test_type_desc", + "extra_specs": { + "capabilities": "gpu" + } + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "volume_type": { + "name": "test_type", + "extra_specs": {}, + "is_public": true, + "os-volume-type-access:is_public": true, + "id": "6d0ff92a-0007-4780-9ece-acfe5876966a", + "description": "test_type_desc", + "extra_specs": { + "capabilities": "gpu" + } + } +} + `) + }) +} + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} + +func MockUpdateResponse(t *testing.T) { + th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` +{ + "volume_type": { + "name": "vol-type-002", + "description": "volume type 0001", + "is_public": true, + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" + } +}`) + }) +} + +// ExtraSpecsGetBody provides a GET result of the extra_specs for a volume type +const ExtraSpecsGetBody = ` +{ + "extra_specs" : { + "capabilities": "gpu", + "volume_backend_name": "ssd" + } +} +` + +// GetExtraSpecBody provides a GET result of a particular extra_spec for a volume type +const GetExtraSpecBody = ` +{ + "capabilities": "gpu" +} +` + +// UpdatedExtraSpecBody provides an PUT result of a particular updated extra_spec for a volume type +const UpdatedExtraSpecBody = ` +{ + "capabilities": "gpu-2" +} +` + +// ExtraSpecs is the expected extra_specs returned from GET on a volume type's extra_specs +var ExtraSpecs = map[string]string{ + "capabilities": "gpu", + "volume_backend_name": "ssd", +} + +// ExtraSpec is the expected extra_spec returned from GET on a volume type's extra_specs +var ExtraSpec = map[string]string{ + "capabilities": "gpu", +} + +// UpdatedExtraSpec is the expected extra_spec returned from PUT on a volume type's extra_specs +var UpdatedExtraSpec = map[string]string{ + "capabilities": "gpu-2", +} + +func HandleExtraSpecsListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/types/1/extra_specs", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExtraSpecsGetBody) + }) +} + +func HandleExtraSpecGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/types/1/extra_specs/capabilities", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetExtraSpecBody) + }) +} + +func HandleExtraSpecsCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/types/1/extra_specs", 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, "Accept", "application/json") + th.TestJSONRequest(t, r, `{ + "extra_specs": { + "capabilities": "gpu", + "volume_backend_name": "ssd" + } + }`) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExtraSpecsGetBody) + }) +} + +func HandleExtraSpecUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/types/1/extra_specs/capabilities", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, `{ + "capabilities": "gpu-2" + }`) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdatedExtraSpecBody) + }) +} + +func HandleExtraSpecDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/types/1/extra_specs/capabilities", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/evs/v3/volumetypes/testing/requests_test.go b/openstack/evs/v3/volumetypes/testing/requests_test.go new file mode 100644 index 000000000..eb6f2e7c0 --- /dev/null +++ b/openstack/evs/v3/volumetypes/testing/requests_test.go @@ -0,0 +1,278 @@ +package testing + +import ( + "fmt" + "net/http" + "reflect" + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + pages := 0 + err := volumetypes.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + pages++ + actual, err := volumetypes.ExtractVolumeTypes(page) + if err != nil { + return false, err + } + expected := []volumetypes.VolumeType{ + { + ID: "6685584b-1eac-4da6-b5c3-555430cf68ff", + Name: "SSD", + ExtraSpecs: map[string]string{"volume_backend_name": "lvmdriver-1"}, + IsPublic: true, + Description: "", + QosSpecID: "", + PublicAccess: true, + }, { + ID: "8eb69a46-df97-4e41-9586-9a40a7533803", + Name: "SATA", + ExtraSpecs: map[string]string{"volume_backend_name": "lvmdriver-1"}, + IsPublic: true, + Description: "", + QosSpecID: "", + PublicAccess: true, + }, + } + th.CheckDeepEquals(t, expected, actual) + return true, nil + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, pages, 1) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + v, err := volumetypes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, v.Name, "vol-type-001") + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertEquals(t, v.ExtraSpecs["capabilities"], "gpu") + th.AssertEquals(t, v.QosSpecID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertEquals(t, v.PublicAccess, true) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + var isPublic = true + + options := &volumetypes.CreateOpts{ + Name: "test_type", + IsPublic: &isPublic, + Description: "test_type_desc", + ExtraSpecs: map[string]string{"capabilities": "gpu"}, + } + + n, err := volumetypes.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Name, "test_type") + th.AssertEquals(t, n.Description, "test_type_desc") + th.AssertEquals(t, n.IsPublic, true) + th.AssertEquals(t, n.PublicAccess, true) + th.AssertEquals(t, n.ID, "6d0ff92a-0007-4780-9ece-acfe5876966a") + th.AssertEquals(t, n.ExtraSpecs["capabilities"], "gpu") +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := volumetypes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateResponse(t) + + var isPublic = true + var name = "vol-type-002" + options := volumetypes.UpdateOpts{ + Name: &name, + IsPublic: &isPublic, + } + + v, err := volumetypes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + th.AssertNoErr(t, err) + th.CheckEquals(t, "vol-type-002", v.Name) + th.CheckEquals(t, true, v.IsPublic) +} + +func TestVolumeTypeExtraSpecsList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecsListSuccessfully(t) + + expected := ExtraSpecs + actual, err := volumetypes.ListExtraSpecs(client.ServiceClient(), "1").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestVolumeTypeExtraSpecGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecGetSuccessfully(t) + + expected := ExtraSpec + actual, err := volumetypes.GetExtraSpec(client.ServiceClient(), "1", "capabilities").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestVolumeTypeExtraSpecsCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecsCreateSuccessfully(t) + + createOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "gpu", + "volume_backend_name": "ssd", + } + expected := ExtraSpecs + actual, err := volumetypes.CreateExtraSpecs(client.ServiceClient(), "1", createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestVolumeTypeExtraSpecUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecUpdateSuccessfully(t) + + updateOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "gpu-2", + } + expected := UpdatedExtraSpec + actual, err := volumetypes.UpdateExtraSpec(client.ServiceClient(), "1", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestVolumeTypeExtraSpecDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecDeleteSuccessfully(t) + + res := volumetypes.DeleteExtraSpec(client.ServiceClient(), "1", "capabilities") + th.AssertNoErr(t, res.Err) +} + +func TestVolumeTypeListAccesses(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/os-volume-type-access", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, ` + { + "volume_type_access": [ + { + "project_id": "6f70656e737461636b20342065766572", + "volume_type_id": "a5082c24-2a27-43a4-b48e-fcec1240e36b" + } + ] + } + `) + }) + + expected := []volumetypes.VolumeTypeAccess{ + { + VolumeTypeID: "a5082c24-2a27-43a4-b48e-fcec1240e36b", + ProjectID: "6f70656e737461636b20342065766572", + }, + } + + allPages, err := volumetypes.ListAccesses(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b").AllPages() + th.AssertNoErr(t, err) + + actual, err := volumetypes.ExtractAccesses(allPages) + th.AssertNoErr(t, err) + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} + +func TestVolumeTypeAddAccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "accept", "application/json") + th.TestJSONRequest(t, r, ` + { + "addProjectAccess": { + "project": "6f70656e737461636b20342065766572" + } + } + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) + + addAccessOpts := volumetypes.AddAccessOpts{ + Project: "6f70656e737461636b20342065766572", + } + + err := volumetypes.AddAccess(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", addAccessOpts).ExtractErr() + th.AssertNoErr(t, err) + +} + +func TestVolumeTypeRemoveAccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "accept", "application/json") + th.TestJSONRequest(t, r, ` + { + "removeProjectAccess": { + "project": "6f70656e737461636b20342065766572" + } + } + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) + + removeAccessOpts := volumetypes.RemoveAccessOpts{ + Project: "6f70656e737461636b20342065766572", + } + + err := volumetypes.RemoveAccess(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", removeAccessOpts).ExtractErr() + th.AssertNoErr(t, err) + +} diff --git a/openstack/evs/v3/volumetypes/urls.go b/openstack/evs/v3/volumetypes/urls.go new file mode 100644 index 000000000..c63ee47e6 --- /dev/null +++ b/openstack/evs/v3/volumetypes/urls.go @@ -0,0 +1,51 @@ +package volumetypes + +import "github.com/gophercloud/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("types") +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("types", id) +} + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("types") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("types", id) +} + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("types", id) +} + +func extraSpecsListURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("types", id, "extra_specs") +} + +func extraSpecsGetURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("types", id, "extra_specs", key) +} + +func extraSpecsCreateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("types", id, "extra_specs") +} + +func extraSpecUpdateURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("types", id, "extra_specs", key) +} + +func extraSpecDeleteURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("types", id, "extra_specs", key) +} + +func accessURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("types", id, "os-volume-type-access") +} + +func accessActionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("types", id, "action") +} From 1f1b77bf930c2c038d1efd9cc452c26149069abc Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 13:22:38 +0100 Subject: [PATCH 02/51] Replace --- openstack/evs/apiversions/doc.go | 30 -- openstack/evs/apiversions/errors.go | 23 -- openstack/evs/apiversions/requests.go | 13 - openstack/evs/apiversions/results.go | 64 --- openstack/evs/apiversions/testing/doc.go | 2 - openstack/evs/apiversions/testing/fixtures.go | 141 ------- .../evs/apiversions/testing/requests_test.go | 119 ------ openstack/evs/apiversions/urls.go | 14 - .../extensions/availabilityzones/requests.go | 6 +- .../extensions/availabilityzones/results.go | 2 +- .../availabilityzones/testing/doc.go | 2 - .../availabilityzones/testing/fixtures.go | 52 --- .../testing/requests_test.go | 25 -- .../evs/extensions/availabilityzones/urls.go | 4 +- openstack/evs/extensions/backups/requests.go | 58 +-- openstack/evs/extensions/backups/results.go | 20 +- .../extensions/backups/testing/fixtures.go | 300 -------------- .../backups/testing/requests_test.go | 189 --------- openstack/evs/extensions/backups/urls.go | 20 +- openstack/evs/extensions/limits/requests.go | 6 +- openstack/evs/extensions/limits/results.go | 4 +- .../evs/extensions/limits/testing/fixtures.go | 129 ------ .../limits/testing/requests_test.go | 19 - openstack/evs/extensions/limits/urls.go | 4 +- openstack/evs/extensions/quotasets/doc.go | 10 +- .../evs/extensions/quotasets/requests.go | 28 +- openstack/evs/extensions/quotasets/results.go | 12 +- .../evs/extensions/quotasets/testing/doc.go | 2 - .../extensions/quotasets/testing/fixtures.go | 179 --------- .../quotasets/testing/requests_test.go | 80 ---- openstack/evs/extensions/quotasets/urls.go | 10 +- .../evs/extensions/schedulerhints/requests.go | 8 +- .../extensions/schedulerhints/testing/doc.go | 2 - .../schedulerhints/testing/requests_test.go | 58 --- .../evs/extensions/schedulerstats/requests.go | 8 +- .../evs/extensions/schedulerstats/results.go | 2 +- .../schedulerstats/testing/fixtures.go | 110 ------ .../schedulerstats/testing/requests_test.go | 38 -- .../evs/extensions/schedulerstats/urls.go | 4 +- openstack/evs/extensions/services/requests.go | 8 +- openstack/evs/extensions/services/results.go | 6 +- .../extensions/services/testing/fixtures.go | 97 ----- .../services/testing/requests_test.go | 41 -- openstack/evs/extensions/services/urls.go | 4 +- openstack/evs/extensions/volumeactions/doc.go | 4 +- .../evs/extensions/volumeactions/requests.go | 104 ++--- .../evs/extensions/volumeactions/results.go | 38 +- .../extensions/volumeactions/testing/doc.go | 2 - .../volumeactions/testing/fixtures.go | 372 ------------------ .../volumeactions/testing/requests_test.go | 226 ----------- .../evs/extensions/volumeactions/urls.go | 4 +- .../extensions/volumetransfers/requests.go | 32 +- .../evs/extensions/volumetransfers/results.go | 14 +- .../volumetransfers/testing/fixtures.go | 216 ---------- .../volumetransfers/testing/requests_test.go | 90 ----- .../evs/extensions/volumetransfers/urls.go | 12 +- openstack/evs/v1/apiversions/get.go | 19 - openstack/evs/v1/apiversions/list.go | 26 -- .../evs/v1/apiversions/testing/fixtures.go | 91 ----- .../v1/apiversions/testing/requests_test.go | 54 --- openstack/evs/v3/attachments/requests.go | 40 +- openstack/evs/v3/attachments/results.go | 14 +- .../evs/v3/attachments/testing/fixtures.go | 233 ----------- .../v3/attachments/testing/requests_test.go | 119 ------ openstack/evs/v3/attachments/urls.go | 14 +- openstack/evs/v3/attachments/util.go | 6 +- openstack/evs/v3/qos/requests.go | 68 ++-- openstack/evs/v3/qos/results.go | 20 +- openstack/evs/v3/qos/testing/doc.go | 2 - openstack/evs/v3/qos/testing/fixtures.go | 232 ----------- openstack/evs/v3/qos/testing/requests_test.go | 178 --------- openstack/evs/v3/qos/urls.go | 22 +- openstack/evs/v3/snapshots/requests.go | 40 +- openstack/evs/v3/snapshots/results.go | 16 +- openstack/evs/v3/snapshots/testing/doc.go | 2 - .../evs/v3/snapshots/testing/fixtures.go | 178 --------- .../evs/v3/snapshots/testing/requests_test.go | 131 ------ openstack/evs/v3/snapshots/urls.go | 16 +- openstack/evs/v3/snapshots/util.go | 6 +- openstack/evs/v3/volumes/requests.go | 34 +- openstack/evs/v3/volumes/results.go | 18 +- openstack/evs/v3/volumes/testing/doc.go | 2 - openstack/evs/v3/volumes/testing/fixtures.go | 265 ------------- .../evs/v3/volumes/testing/requests_test.go | 282 ------------- openstack/evs/v3/volumes/urls.go | 12 +- openstack/evs/v3/volumes/util.go | 6 +- openstack/evs/v3/volumetypes/requests.go | 78 ++-- openstack/evs/v3/volumetypes/results.go | 22 +- openstack/evs/v3/volumetypes/testing/doc.go | 2 - .../evs/v3/volumetypes/testing/fixtures.go | 260 ------------ .../v3/volumetypes/testing/requests_test.go | 278 ------------- openstack/evs/v3/volumetypes/urls.go | 26 +- 92 files changed, 445 insertions(+), 5434 deletions(-) delete mode 100644 openstack/evs/apiversions/doc.go delete mode 100644 openstack/evs/apiversions/errors.go delete mode 100644 openstack/evs/apiversions/requests.go delete mode 100644 openstack/evs/apiversions/results.go delete mode 100644 openstack/evs/apiversions/testing/doc.go delete mode 100644 openstack/evs/apiversions/testing/fixtures.go delete mode 100644 openstack/evs/apiversions/testing/requests_test.go delete mode 100644 openstack/evs/apiversions/urls.go delete mode 100644 openstack/evs/extensions/availabilityzones/testing/doc.go delete mode 100644 openstack/evs/extensions/availabilityzones/testing/fixtures.go delete mode 100644 openstack/evs/extensions/availabilityzones/testing/requests_test.go delete mode 100644 openstack/evs/extensions/backups/testing/fixtures.go delete mode 100644 openstack/evs/extensions/backups/testing/requests_test.go delete mode 100644 openstack/evs/extensions/limits/testing/fixtures.go delete mode 100644 openstack/evs/extensions/limits/testing/requests_test.go delete mode 100644 openstack/evs/extensions/quotasets/testing/doc.go delete mode 100644 openstack/evs/extensions/quotasets/testing/fixtures.go delete mode 100644 openstack/evs/extensions/quotasets/testing/requests_test.go delete mode 100644 openstack/evs/extensions/schedulerhints/testing/doc.go delete mode 100644 openstack/evs/extensions/schedulerhints/testing/requests_test.go delete mode 100644 openstack/evs/extensions/schedulerstats/testing/fixtures.go delete mode 100644 openstack/evs/extensions/schedulerstats/testing/requests_test.go delete mode 100644 openstack/evs/extensions/services/testing/fixtures.go delete mode 100644 openstack/evs/extensions/services/testing/requests_test.go delete mode 100644 openstack/evs/extensions/volumeactions/testing/doc.go delete mode 100644 openstack/evs/extensions/volumeactions/testing/fixtures.go delete mode 100644 openstack/evs/extensions/volumeactions/testing/requests_test.go delete mode 100644 openstack/evs/extensions/volumetransfers/testing/fixtures.go delete mode 100644 openstack/evs/extensions/volumetransfers/testing/requests_test.go delete mode 100644 openstack/evs/v1/apiversions/get.go delete mode 100644 openstack/evs/v1/apiversions/list.go delete mode 100644 openstack/evs/v1/apiversions/testing/fixtures.go delete mode 100644 openstack/evs/v1/apiversions/testing/requests_test.go delete mode 100644 openstack/evs/v3/attachments/testing/fixtures.go delete mode 100644 openstack/evs/v3/attachments/testing/requests_test.go delete mode 100644 openstack/evs/v3/qos/testing/doc.go delete mode 100644 openstack/evs/v3/qos/testing/fixtures.go delete mode 100644 openstack/evs/v3/qos/testing/requests_test.go delete mode 100644 openstack/evs/v3/snapshots/testing/doc.go delete mode 100644 openstack/evs/v3/snapshots/testing/fixtures.go delete mode 100644 openstack/evs/v3/snapshots/testing/requests_test.go delete mode 100644 openstack/evs/v3/volumes/testing/doc.go delete mode 100644 openstack/evs/v3/volumes/testing/fixtures.go delete mode 100644 openstack/evs/v3/volumes/testing/requests_test.go delete mode 100644 openstack/evs/v3/volumetypes/testing/doc.go delete mode 100644 openstack/evs/v3/volumetypes/testing/fixtures.go delete mode 100644 openstack/evs/v3/volumetypes/testing/requests_test.go diff --git a/openstack/evs/apiversions/doc.go b/openstack/evs/apiversions/doc.go deleted file mode 100644 index 7f0546361..000000000 --- a/openstack/evs/apiversions/doc.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Package apiversions provides information and interaction with the different -API versions for the OpenStack Block Storage service, code-named Cinder. - -Example of Retrieving all API Versions - - allPages, err := apiversions.List(client).AllPages() - if err != nil { - panic("unable to get API versions: " + err.Error()) - } - - allVersions, err := apiversions.ExtractAPIVersions(allPages) - if err != nil { - panic("unable to extract API versions: " + err.Error()) - } - - for _, version := range allVersions { - fmt.Printf("%+v\n", version) - } - -Example of Retrieving an API Version - - version, err := apiversions.Get(client, "v3").Extract() - if err != nil { - panic("unable to get API version: " + err.Error()) - } - - fmt.Printf("%+v\n", version) -*/ -package apiversions diff --git a/openstack/evs/apiversions/errors.go b/openstack/evs/apiversions/errors.go deleted file mode 100644 index 8f0f7628d..000000000 --- a/openstack/evs/apiversions/errors.go +++ /dev/null @@ -1,23 +0,0 @@ -package apiversions - -import ( - "fmt" -) - -// ErrVersionNotFound is the error when the requested API version -// could not be found. -type ErrVersionNotFound struct{} - -func (e ErrVersionNotFound) Error() string { - return fmt.Sprintf("Unable to find requested API version") -} - -// ErrMultipleVersionsFound is the error when a request for an API -// version returns multiple results. -type ErrMultipleVersionsFound struct { - Count int -} - -func (e ErrMultipleVersionsFound) Error() string { - return fmt.Sprintf("Found %d API versions", e.Count) -} diff --git a/openstack/evs/apiversions/requests.go b/openstack/evs/apiversions/requests.go deleted file mode 100644 index d2d3851d1..000000000 --- a/openstack/evs/apiversions/requests.go +++ /dev/null @@ -1,13 +0,0 @@ -package apiversions - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// List lists all the Cinder API versions available to end-users. -func List(c *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { - return APIVersionPage{pagination.SinglePageBase(r)} - }) -} diff --git a/openstack/evs/apiversions/results.go b/openstack/evs/apiversions/results.go deleted file mode 100644 index 08adcb72c..000000000 --- a/openstack/evs/apiversions/results.go +++ /dev/null @@ -1,64 +0,0 @@ -package apiversions - -import ( - "time" - - "github.com/gophercloud/gophercloud/pagination" -) - -// APIVersion represents an API version for Cinder. -type APIVersion struct { - // ID is the unique identifier of the API version. - ID string `json:"id"` - - // MinVersion is the minimum microversion supported. - MinVersion string `json:"min_version"` - - // Status represents the status of the API version. - Status string `json:"status"` - - // Updated is the date the API version was updated. - Updated time.Time `json:"updated"` - - // Version is the current version and microversion. - Version string `json:"version"` -} - -// APIVersionPage is the page returned by a pager when traversing over a -// collection of API versions. -type APIVersionPage struct { - pagination.SinglePageBase -} - -// IsEmpty checks whether an APIVersionPage struct is empty. -func (r APIVersionPage) IsEmpty() (bool, error) { - is, err := ExtractAPIVersions(r) - return len(is) == 0, err -} - -// ExtractAPIVersions takes a collection page, extracts all of the elements, -// and returns them a slice of APIVersion structs. It is effectively a cast. -func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { - var s struct { - Versions []APIVersion `json:"versions"` - } - err := (r.(APIVersionPage)).ExtractInto(&s) - return s.Versions, err -} - -// ExtractAPIVersion takes a List result and extracts a single requested -// version, which is returned as an APIVersion -func ExtractAPIVersion(r pagination.Page, v string) (*APIVersion, error) { - allVersions, err := ExtractAPIVersions(r) - if err != nil { - return nil, err - } - - for _, version := range allVersions { - if version.ID == v { - return &version, nil - } - } - - return nil, ErrVersionNotFound{} -} diff --git a/openstack/evs/apiversions/testing/doc.go b/openstack/evs/apiversions/testing/doc.go deleted file mode 100644 index 12e4bda0f..000000000 --- a/openstack/evs/apiversions/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// apiversions_v1 -package testing diff --git a/openstack/evs/apiversions/testing/fixtures.go b/openstack/evs/apiversions/testing/fixtures.go deleted file mode 100644 index 3be1ee66b..000000000 --- a/openstack/evs/apiversions/testing/fixtures.go +++ /dev/null @@ -1,141 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -const APIListResponse = ` -{ - "versions": [ - { - "id": "v1.0", - "links": [ - { - "href": "http://docs.openstack.org/", - "rel": "describedby", - "type": "text/html" - }, - { - "href": "http://localhost:8776/v1/", - "rel": "self" - } - ], - "media-types": [ - { - "base": "application/json", - "type": "application/vnd.openstack.volume+json;version=1" - } - ], - "min_version": "", - "status": "DEPRECATED", - "updated": "2016-05-02T20:25:19Z", - "version": "" - }, - { - "id": "v2.0", - "links": [ - { - "href": "http://docs.openstack.org/", - "rel": "describedby", - "type": "text/html" - }, - { - "href": "http://localhost:8776/v2/", - "rel": "self" - } - ], - "media-types": [ - { - "base": "application/json", - "type": "application/vnd.openstack.volume+json;version=1" - } - ], - "min_version": "", - "status": "SUPPORTED", - "updated": "2014-06-28T12:20:21Z", - "version": "" - }, - { - "id": "v3.0", - "links": [ - { - "href": "http://docs.openstack.org/", - "rel": "describedby", - "type": "text/html" - }, - { - "href": "http://localhost:8776/v3/", - "rel": "self" - } - ], - "media-types": [ - { - "base": "application/json", - "type": "application/vnd.openstack.volume+json;version=1" - } - ], - "min_version": "3.0", - "status": "CURRENT", - "updated": "2016-02-08T12:20:21Z", - "version": "3.27" - } - ] -} -` - -const APIListOldResponse = ` -{ - "versions": [ - { - "status": "CURRENT", - "updated": "2012-01-04T11:33:21Z", - "id": "v1.0", - "links": [ - { - "href": "http://23.253.228.211:8776/v1/", - "rel": "self" - } - ] - }, - { - "status": "CURRENT", - "updated": "2012-11-21T11:33:21Z", - "id": "v2.0", - "links": [ - { - "href": "http://23.253.228.211:8776/v2/", - "rel": "self" - } - ] - } - ] -}` - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, APIListResponse) - }) -} - -func MockListOldResponse(t *testing.T) { - th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, APIListOldResponse) - }) -} diff --git a/openstack/evs/apiversions/testing/requests_test.go b/openstack/evs/apiversions/testing/requests_test.go deleted file mode 100644 index ca9688243..000000000 --- a/openstack/evs/apiversions/testing/requests_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package testing - -import ( - "testing" - "time" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestListVersions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() - th.AssertNoErr(t, err) - actual, err := apiversions.ExtractAPIVersions(allVersions) - th.AssertNoErr(t, err) - - expected := []apiversions.APIVersion{ - { - ID: "v1.0", - Status: "DEPRECATED", - Updated: time.Date(2016, 5, 2, 20, 25, 19, 0, time.UTC), - }, - { - ID: "v2.0", - Status: "SUPPORTED", - Updated: time.Date(2014, 6, 28, 12, 20, 21, 0, time.UTC), - }, - { - ID: "v3.0", - MinVersion: "3.0", - Status: "CURRENT", - Updated: time.Date(2016, 2, 8, 12, 20, 21, 0, time.UTC), - Version: "3.27", - }, - } - - th.AssertDeepEquals(t, expected, actual) -} - -func TestListOldVersions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListOldResponse(t) - - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() - th.AssertNoErr(t, err) - actual, err := apiversions.ExtractAPIVersions(allVersions) - th.AssertNoErr(t, err) - - expected := []apiversions.APIVersion{ - { - ID: "v1.0", - Status: "CURRENT", - Updated: time.Date(2012, 1, 4, 11, 33, 21, 0, time.UTC), - }, - { - ID: "v2.0", - Status: "CURRENT", - Updated: time.Date(2012, 11, 21, 11, 33, 21, 0, time.UTC), - }, - } - - th.AssertDeepEquals(t, expected, actual) -} - -func TestGetVersion(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() - th.AssertNoErr(t, err) - actual, err := apiversions.ExtractAPIVersion(allVersions, "v3.0") - th.AssertNoErr(t, err) - - expected := apiversions.APIVersion{ - ID: "v3.0", - MinVersion: "3.0", - Status: "CURRENT", - Updated: time.Date(2016, 2, 8, 12, 20, 21, 0, time.UTC), - Version: "3.27", - } - - th.AssertEquals(t, actual.ID, expected.ID) - th.AssertEquals(t, actual.Status, expected.Status) - th.AssertEquals(t, actual.Updated, expected.Updated) -} - -func TestGetOldVersion(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListOldResponse(t) - - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() - th.AssertNoErr(t, err) - actual, err := apiversions.ExtractAPIVersion(allVersions, "v2.0") - th.AssertNoErr(t, err) - - expected := apiversions.APIVersion{ - ID: "v2.0", - MinVersion: "", - Status: "CURRENT", - Updated: time.Date(2012, 11, 21, 11, 33, 21, 0, time.UTC), - Version: "", - } - - th.AssertEquals(t, actual.ID, expected.ID) - th.AssertEquals(t, actual.Status, expected.Status) - th.AssertEquals(t, actual.Updated, expected.Updated) -} diff --git a/openstack/evs/apiversions/urls.go b/openstack/evs/apiversions/urls.go deleted file mode 100644 index a6a35d422..000000000 --- a/openstack/evs/apiversions/urls.go +++ /dev/null @@ -1,14 +0,0 @@ -package apiversions - -import ( - "strings" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" -) - -func listURL(c *gophercloud.ServiceClient) string { - baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) - endpoint := strings.TrimRight(baseEndpoint, "/") + "/" - return endpoint -} diff --git a/openstack/evs/extensions/availabilityzones/requests.go b/openstack/evs/extensions/availabilityzones/requests.go index df10b856e..d4b270eec 100644 --- a/openstack/evs/extensions/availabilityzones/requests.go +++ b/openstack/evs/extensions/availabilityzones/requests.go @@ -1,12 +1,12 @@ package availabilityzones import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // List will return the existing availability zones. -func List(client *gophercloud.ServiceClient) pagination.Pager { +func List(client *golangsdk.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return AvailabilityZonePage{pagination.SinglePageBase(r)} }) diff --git a/openstack/evs/extensions/availabilityzones/results.go b/openstack/evs/extensions/availabilityzones/results.go index 0e115411c..59599f00c 100644 --- a/openstack/evs/extensions/availabilityzones/results.go +++ b/openstack/evs/extensions/availabilityzones/results.go @@ -1,7 +1,7 @@ package availabilityzones import ( - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // ZoneState represents the current state of the availability zone. diff --git a/openstack/evs/extensions/availabilityzones/testing/doc.go b/openstack/evs/extensions/availabilityzones/testing/doc.go deleted file mode 100644 index a4408d7a0..000000000 --- a/openstack/evs/extensions/availabilityzones/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// availabilityzones unittests -package testing diff --git a/openstack/evs/extensions/availabilityzones/testing/fixtures.go b/openstack/evs/extensions/availabilityzones/testing/fixtures.go deleted file mode 100644 index 4b500e484..000000000 --- a/openstack/evs/extensions/availabilityzones/testing/fixtures.go +++ /dev/null @@ -1,52 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - az "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/availabilityzones" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -const GetOutput = ` -{ - "availabilityZoneInfo": [ - { - "zoneName": "internal", - "zoneState": { - "available": true - } - }, - { - "zoneName": "nova", - "zoneState": { - "available": true - } - } - ] -}` - -var AZResult = []az.AvailabilityZone{ - { - ZoneName: "internal", - ZoneState: az.ZoneState{Available: true}, - }, - { - ZoneName: "nova", - ZoneState: az.ZoneState{Available: true}, - }, -} - -// HandleGetSuccessfully configures the test server to respond to a Get request -// for availability zone information. -func HandleGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-availability-zone", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) - }) -} diff --git a/openstack/evs/extensions/availabilityzones/testing/requests_test.go b/openstack/evs/extensions/availabilityzones/testing/requests_test.go deleted file mode 100644 index 39f41bf09..000000000 --- a/openstack/evs/extensions/availabilityzones/testing/requests_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package testing - -import ( - "testing" - - az "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/availabilityzones" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -// Verifies that availability zones can be listed correctly -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGetSuccessfully(t) - - allPages, err := az.List(client.ServiceClient()).AllPages() - th.AssertNoErr(t, err) - - actual, err := az.ExtractAvailabilityZones(allPages) - th.AssertNoErr(t, err) - - th.CheckDeepEquals(t, AZResult, actual) -} diff --git a/openstack/evs/extensions/availabilityzones/urls.go b/openstack/evs/extensions/availabilityzones/urls.go index fb4cdcf4e..d298c15d5 100644 --- a/openstack/evs/extensions/availabilityzones/urls.go +++ b/openstack/evs/extensions/availabilityzones/urls.go @@ -1,7 +1,7 @@ package availabilityzones -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" -func listURL(c *gophercloud.ServiceClient) string { +func listURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("os-availability-zone") } diff --git a/openstack/evs/extensions/backups/requests.go b/openstack/evs/extensions/backups/requests.go index 4eb123b9f..2ceccc08b 100644 --- a/openstack/evs/extensions/backups/requests.go +++ b/openstack/evs/extensions/backups/requests.go @@ -1,8 +1,8 @@ package backups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the @@ -19,7 +19,7 @@ type CreateOpts struct { VolumeID string `json:"volume_id" required:"true"` // Force will force the creation of a backup regardless of the - //volume's status. + // volume's status. Force bool `json:"force,omitempty"` // Name is the name of the backup. @@ -49,37 +49,37 @@ type CreateOpts struct { // ToBackupCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToBackupCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "backup") + return golangsdk.BuildRequestBody(opts, "backup") } // Create will create a new Backup based on the values in CreateOpts. To // extract the Backup object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToBackupCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Delete will delete the existing Backup with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { +func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Get retrieves the Backup with the provided ID. To extract the Backup // object from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { +func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -125,13 +125,13 @@ type ListOpts struct { // ToBackupListQuery formats a ListOpts into a query string. func (opts ListOpts) ToBackupListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // List returns Backups optionally limited by the conditions provided in // ListOpts. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { +func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToBackupListQuery() @@ -174,13 +174,13 @@ type ListDetailOpts struct { // ToBackupListDetailQuery formats a ListDetailOpts into a query string. func (opts ListDetailOpts) ToBackupListDetailQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // ListDetail returns more detailed information about Backups optionally // limited by the conditions provided in ListDetailOpts. -func ListDetail(client *gophercloud.ServiceClient, opts ListDetailOptsBuilder) pagination.Pager { +func ListDetail(client *golangsdk.ServiceClient, opts ListDetailOptsBuilder) pagination.Pager { url := listDetailURL(client) if opts != nil { query, err := opts.ToBackupListDetailQuery() @@ -216,23 +216,23 @@ type UpdateOpts struct { // ToBackupUpdateMap assembles a request body based on the contents of // an UpdateOpts. func (opts UpdateOpts) ToBackupUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "") + return golangsdk.BuildRequestBody(opts, "") } // Update will update the Backup with provided information. To extract // the updated Backup from the response, call the Extract method on the // UpdateResult. // Requires microversion 3.9 or later. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToBackupUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -249,30 +249,30 @@ type RestoreOpts struct { // ToRestoreMap assembles a request body based on the contents of a // RestoreOpts. func (opts RestoreOpts) ToRestoreMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "restore") + return golangsdk.BuildRequestBody(opts, "restore") } // RestoreFromBackup will restore a Backup to a volume based on the values in // RestoreOpts. To extract the Restore object from the response, call the // Extract method on the RestoreResult. -func RestoreFromBackup(client *gophercloud.ServiceClient, id string, opts RestoreOpts) (r RestoreResult) { +func RestoreFromBackup(client *golangsdk.ServiceClient, id string, opts RestoreOpts) (r RestoreResult) { b, err := opts.ToRestoreMap() if err != nil { r.Err = err return } - resp, err := client.Post(restoreURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(restoreURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Export will export a Backup information. To extract the Backup export record // object from the response, call the Extract method on the ExportResult. -func Export(client *gophercloud.ServiceClient, id string) (r ExportResult) { +func Export(client *golangsdk.ServiceClient, id string) (r ExportResult) { resp, err := client.Get(exportURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -283,21 +283,21 @@ type ImportOpts BackupRecord // ToBackupImportMap assembles a request body based on the contents of a // ImportOpts. func (opts ImportOpts) ToBackupImportMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "backup-record") + return golangsdk.BuildRequestBody(opts, "backup-record") } // Import will import a Backup data to a backup based on the values in // ImportOpts. To extract the Backup object from the response, call the // Extract method on the ImportResult. -func Import(client *gophercloud.ServiceClient, opts ImportOpts) (r ImportResult) { +func Import(client *golangsdk.ServiceClient, opts ImportOpts) (r ImportResult) { b, err := opts.ToBackupImportMap() if err != nil { r.Err = err return } - resp, err := client.Post(importURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(importURL(client), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{201}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } diff --git a/openstack/evs/extensions/backups/results.go b/openstack/evs/extensions/backups/results.go index a575498de..4cc687287 100644 --- a/openstack/evs/extensions/backups/results.go +++ b/openstack/evs/extensions/backups/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Backup contains all the information associated with a Cinder Backup. @@ -81,7 +81,7 @@ type GetResult struct { // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // BackupPage is a pagination.Pager that is returned from a call to the List function. @@ -94,9 +94,9 @@ func (r *Backup) UnmarshalJSON(b []byte) error { type tmp Backup var s struct { tmp - CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` - DataTimestamp gophercloud.JSONRFC3339MilliNoZ `json:"data_timestamp"` + CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` + DataTimestamp golangsdk.JSONRFC3339MilliNoZ `json:"data_timestamp"` } err := json.Unmarshal(b, &s) if err != nil { @@ -119,13 +119,13 @@ func (r BackupPage) IsEmpty() (bool, error) { func (page BackupPage) NextPageURL() (string, error) { var s struct { - Links []gophercloud.Link `json:"backups_links"` + Links []golangsdk.Link `json:"backups_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } - return gophercloud.ExtractNextURL(s.Links) + return golangsdk.ExtractNextURL(s.Links) } // ExtractBackups extracts and returns Backups. It is used while iterating over a backups.List call. @@ -141,7 +141,7 @@ type UpdateResult struct { } type commonResult struct { - gophercloud.Result + golangsdk.Result } // Extract will get the Backup object out of the commonResult object. @@ -221,7 +221,7 @@ type ImportResponse struct { // ImportResult contains the response body and error from an import request. type ImportResult struct { - gophercloud.Result + golangsdk.Result } // Extract will get the Backup object out of the commonResult object. diff --git a/openstack/evs/extensions/backups/testing/fixtures.go b/openstack/evs/extensions/backups/testing/fixtures.go deleted file mode 100644 index 94cd9b52f..000000000 --- a/openstack/evs/extensions/backups/testing/fixtures.go +++ /dev/null @@ -1,300 +0,0 @@ -package testing - -import ( - "encoding/json" - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -const ListResponse = ` -{ - "backups": [ - { - "id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "name": "backup-001" - }, - { - "id": "96c3bda7-c82a-4f50-be73-ca7621794835", - "name": "backup-002" - } - ], - "backups_links": [ - { - "href": "%s/backups?marker=1", - "rel": "next" - } - ] -} -` - -const ListDetailResponse = ` -{ - "backups": [ - { - "id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "name": "backup-001", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "description": "Daily Backup", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - }, - { - "id": "96c3bda7-c82a-4f50-be73-ca7621794835", - "name": "backup-002", - "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", - "description": "Weekly Backup", - "status": "available", - "size": 25, - "created_at": "2017-05-30T03:35:03.000000" - } - ], - "backups_links": [ - { - "href": "%s/backups/detail?marker=1", - "rel": "next" - } - ] -} -` - -const GetResponse = ` -{ - "backup": { - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "name": "backup-001", - "description": "Daily backup", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - } -} -` -const CreateRequest = ` -{ - "backup": { - "volume_id": "1234", - "name": "backup-001" - } -} -` - -const CreateResponse = ` -{ - "backup": { - "volume_id": "1234", - "name": "backup-001", - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "description": "Daily backup", - "volume_id": "1234", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - } -} -` - -const RestoreRequest = ` -{ - "restore": { - "name": "vol-001", - "volume_id": "1234" - } -} -` - -const RestoreResponse = ` -{ - "restore": { - "backup_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "volume_id": "1234", - "volume_name": "vol-001" - } -} -` - -const ExportResponse = ` -{ - "backup-record": { - "backup_service": "cinder.backup.drivers.swift.SwiftBackupDriver", - "backup_url": "eyJpZCI6ImQzMjAxOWQzLWJjNmUtNDMxOS05YzFkLTY3MjJmYzEzNmEyMiIsInZvbHVtZV9pZCI6ImNmOWJjNmZhLWM1YmMtNDFmNi1iYzRlLTZlNzZjMGJlYTk1OSIsInNuYXBzaG90X2lkIjpudWxsLCJzdGF0dXMiOiJhdmFpbGFibGUiLCJzaXplIjoxLCJvYmplY3RfY291bnQiOjIsImNvbnRhaW5lciI6Im15LXRlc3QtYmFja3VwIiwic2VydmljZV9tZXRhZGF0YSI6InZvbHVtZV9jZjliYzZmYS1jNWJjLTQxZjYtYmM0ZS02ZTc2YzBiZWE5NTkvMjAyMDAzMTExOTI4NTUvYXpfcmVnaW9uYl9iYWNrdXBfYjg3YmIxZTUtMGQ0ZS00NDVlLWE1NDgtNWFlNzQyNTYyYmFjIiwic2VydmljZSI6ImNpbmRlci5iYWNrdXAuZHJpdmVycy5zd2lmdC5Td2lmdEJhY2t1cERyaXZlciIsImhvc3QiOiJjaW5kZXItYmFja3VwLWhvc3QxIiwidXNlcl9pZCI6IjkzNTE0ZTA0LWEwMjYtNGY2MC04MTc2LTM5NWM4NTk1MDFkZCIsInRlbXBfc25hcHNob3RfaWQiOm51bGwsInRlbXBfdm9sdW1lX2lkIjpudWxsLCJyZXN0b3JlX3ZvbHVtZV9pZCI6bnVsbCwibnVtX2RlcGVuZGVudF9iYWNrdXBzIjpudWxsLCJlbmNyeXB0aW9uX2tleV9pZCI6bnVsbCwicGFyZW50X2lkIjpudWxsLCJkZWxldGVkIjpmYWxzZSwiZGlzcGxheV9uYW1lIjpudWxsLCJkaXNwbGF5X2Rlc2NyaXB0aW9uIjpudWxsLCJkcml2ZXJfaW5mbyI6bnVsbCwiZmFpbF9yZWFzb24iOm51bGwsInByb2plY3RfaWQiOiIxNGYxYzFmNWQxMmI0NzU1Yjk0ZWRlZjc4ZmY4YjMyNSIsIm1ldGFkYXRhIjp7fSwiYXZhaWxhYmlsaXR5X3pvbmUiOiJyZWdpb24xYiIsImNyZWF0ZWRfYXQiOiIyMDIwLTAzLTExVDE5OjI1OjI0WiIsInVwZGF0ZWRfYXQiOiIyMDIwLTAzLTExVDE5OjI5OjA4WiIsImRlbGV0ZWRfYXQiOm51bGwsImRhdGFfdGltZXN0YW1wIjoiMjAyMC0wMy0xMVQxOToyNToyNFoifQ==" - } -} -` - -const ImportRequest = ExportResponse - -const ImportResponse = ` -{ - "backup": { - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "links": [ - { - "href": "https://volume/v2/14f1c1f5d12b4755b94edef78ff8b325/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", - "rel": "self" - }, - { - "href": "https://volume/14f1c1f5d12b4755b94edef78ff8b325/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", - "rel": "bookmark" - } - ], - "name": null - } -} -` - -var ( - status = "available" - availabilityZone = "region1b" - host = "cinder-backup-host1" - serviceMetadata = "volume_cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959/20200311192855/az_regionb_backup_b87bb1e5-0d4e-445e-a548-5ae742562bac" - size = 1 - objectCount = 2 - container = "my-test-backup" - service = "cinder.backup.drivers.swift.SwiftBackupDriver" - backupImport = backups.ImportBackup{ - ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", - Status: &status, - AvailabilityZone: &availabilityZone, - VolumeID: "cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959", - UpdatedAt: time.Date(2020, 3, 11, 19, 29, 8, 0, time.UTC), - Host: &host, - UserID: "93514e04-a026-4f60-8176-395c859501dd", - ServiceMetadata: &serviceMetadata, - Size: &size, - ObjectCount: &objectCount, - Container: &container, - Service: &service, - CreatedAt: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), - DataTimestamp: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), - ProjectID: "14f1c1f5d12b4755b94edef78ff8b325", - Metadata: make(map[string]string), - } - backupURL, _ = json.Marshal(backupImport) -) - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/backups", 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) - - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, ListResponse, th.Server.URL) - case "1": - fmt.Fprintf(w, `{"backups": []}`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -func MockListDetailResponse(t *testing.T) { - th.Mux.HandleFunc("/backups/detail", 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) - - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, ListDetailResponse, th.Server.URL) - case "1": - fmt.Fprintf(w, `{"backups": []}`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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, GetResponse) - }) -} - -func MockCreateResponse(t *testing.T) { - th.Mux.HandleFunc("/backups", 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.StatusAccepted) - - fmt.Fprintf(w, CreateResponse) - }) -} - -func MockRestoreResponse(t *testing.T) { - th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22/restore", 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, RestoreRequest) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, RestoreResponse) - }) -} - -func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusNoContent) - }) -} - -func MockExportResponse(t *testing.T) { - th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22/export_record", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestHeader(t, r, "Accept", "application/json") - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ExportResponse) - }) -} - -func MockImportResponse(t *testing.T) { - th.Mux.HandleFunc("/backups/import_record", 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, ImportRequest) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ImportResponse) - }) -} diff --git a/openstack/evs/extensions/backups/testing/requests_test.go b/openstack/evs/extensions/backups/testing/requests_test.go deleted file mode 100644 index 1adc7a451..000000000 --- a/openstack/evs/extensions/backups/testing/requests_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package testing - -import ( - "encoding/json" - "testing" - "time" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - count := 0 - - err := backups.List(client.ServiceClient(), &backups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := backups.ExtractBackups(page) - if err != nil { - t.Errorf("Failed to extract backups: %v", err) - return false, err - } - - expected := []backups.Backup{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "backup-001", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "backup-002", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - if err != nil { - t.Errorf("EachPage returned error: %s", err) - } - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestListDetail(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListDetailResponse(t) - - count := 0 - - err := backups.ListDetail(client.ServiceClient(), &backups.ListDetailOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := backups.ExtractBackups(page) - if err != nil { - t.Errorf("Failed to extract backups: %v", err) - return false, err - } - - expected := []backups.Backup{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "backup-001", - VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - Status: "available", - Size: 30, - CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), - Description: "Daily Backup", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "backup-002", - VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", - Status: "available", - Size: 25, - CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), - Description: "Weekly Backup", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - if err != nil { - t.Errorf("EachPage returned error: %s", err) - } - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - v, err := backups.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, v.Name, "backup-001") - th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - options := backups.CreateOpts{VolumeID: "1234", Name: "backup-001"} - n, err := backups.Create(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.VolumeID, "1234") - th.AssertEquals(t, n.Name, "backup-001") - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestRestore(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockRestoreResponse(t) - - options := backups.RestoreOpts{VolumeID: "1234", Name: "vol-001"} - n, err := backups.RestoreFromBackup(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.VolumeID, "1234") - th.AssertEquals(t, n.VolumeName, "vol-001") - th.AssertEquals(t, n.BackupID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := backups.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} - -func TestExport(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockExportResponse(t) - - n, err := backups.Export(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.BackupService, "cinder.backup.drivers.swift.SwiftBackupDriver") - th.AssertDeepEquals(t, n.BackupURL, backupURL) - - tmp := backups.ImportBackup{} - err = json.Unmarshal(backupURL, &tmp) - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, tmp, backupImport) -} - -func TestImport(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockImportResponse(t) - - options := backups.ImportOpts{ - BackupService: "cinder.backup.drivers.swift.SwiftBackupDriver", - BackupURL: backupURL, - } - n, err := backups.Import(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} diff --git a/openstack/evs/extensions/backups/urls.go b/openstack/evs/extensions/backups/urls.go index e727de727..a23b8a471 100644 --- a/openstack/evs/extensions/backups/urls.go +++ b/openstack/evs/extensions/backups/urls.go @@ -1,39 +1,39 @@ package backups -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" -func createURL(c *gophercloud.ServiceClient) string { +func createURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("backups") } -func deleteURL(c *gophercloud.ServiceClient, id string) string { +func deleteURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("backups", id) } -func getURL(c *gophercloud.ServiceClient, id string) string { +func getURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("backups", id) } -func listURL(c *gophercloud.ServiceClient) string { +func listURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("backups") } -func listDetailURL(c *gophercloud.ServiceClient) string { +func listDetailURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("backups", "detail") } -func updateURL(c *gophercloud.ServiceClient, id string) string { +func updateURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("backups", id) } -func restoreURL(c *gophercloud.ServiceClient, id string) string { +func restoreURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("backups", id, "restore") } -func exportURL(c *gophercloud.ServiceClient, id string) string { +func exportURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("backups", id, "export_record") } -func importURL(c *gophercloud.ServiceClient) string { +func importURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("backups", "import_record") } diff --git a/openstack/evs/extensions/limits/requests.go b/openstack/evs/extensions/limits/requests.go index 21721f1bc..7d621fdd3 100644 --- a/openstack/evs/extensions/limits/requests.go +++ b/openstack/evs/extensions/limits/requests.go @@ -1,13 +1,13 @@ package limits import ( - "github.com/gophercloud/gophercloud" + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" ) // Get returns the limits about the currently scoped tenant. -func Get(client *gophercloud.ServiceClient) (r GetResult) { +func Get(client *golangsdk.ServiceClient) (r GetResult) { url := getURL(client) resp, err := client.Get(url, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } diff --git a/openstack/evs/extensions/limits/results.go b/openstack/evs/extensions/limits/results.go index f0a74b7ec..2667a0713 100644 --- a/openstack/evs/extensions/limits/results.go +++ b/openstack/evs/extensions/limits/results.go @@ -1,7 +1,7 @@ package limits import ( - "github.com/gophercloud/gophercloud" + "github.com/opentelekomcloud/gophertelekomcloud" ) // Limits is a struct that contains the response of a limit query. @@ -76,5 +76,5 @@ func (r GetResult) Extract() (*Limits, error) { // GetResult is the response from a Get operation. Call its Extract // method to interpret it as an Absolute. type GetResult struct { - gophercloud.Result + golangsdk.Result } diff --git a/openstack/evs/extensions/limits/testing/fixtures.go b/openstack/evs/extensions/limits/testing/fixtures.go deleted file mode 100644 index 2df756eff..000000000 --- a/openstack/evs/extensions/limits/testing/fixtures.go +++ /dev/null @@ -1,129 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/limits" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -// GetOutput is a sample response to a Get call. -const GetOutput = ` -{ - "limits": { - "rate": [ - { - "regex": ".*", - "uri": "*", - "limit": [ - { - "verb": "GET", - "next-available": "1970-01-01T00:00:00", - "unit": "MINUTE", - "value": 10, - "remaining": 10 - }, - { - "verb": "POST", - "next-available": "1970-01-01T00:00:00", - "unit": "HOUR", - "value": 5, - "remaining": 5 - } - ] - }, - { - "regex": "changes-since", - "uri": "changes-since*", - "limit": [ - { - "verb": "GET", - "next-available": "1970-01-01T00:00:00", - "unit": "MINUTE", - "value": 5, - "remaining": 5 - } - ] - } - ], - "absolute": { - "maxTotalVolumes": 40, - "maxTotalSnapshots": 40, - "maxTotalVolumeGigabytes": 1000, - "maxTotalBackups": 10, - "maxTotalBackupGigabytes": 1000, - "totalVolumesUsed": 1, - "totalGigabytesUsed": 100, - "totalSnapshotsUsed": 1, - "totalBackupsUsed": 1, - "totalBackupGigabytesUsed": 50 - } - } -} -` - -// LimitsResult is the result of the limits in GetOutput. -var LimitsResult = limits.Limits{ - Rate: []limits.Rate{ - { - Regex: ".*", - URI: "*", - Limit: []limits.Limit{ - { - Verb: "GET", - NextAvailable: "1970-01-01T00:00:00", - Unit: "MINUTE", - Value: 10, - Remaining: 10, - }, - { - Verb: "POST", - NextAvailable: "1970-01-01T00:00:00", - Unit: "HOUR", - Value: 5, - Remaining: 5, - }, - }, - }, - { - Regex: "changes-since", - URI: "changes-since*", - Limit: []limits.Limit{ - { - Verb: "GET", - NextAvailable: "1970-01-01T00:00:00", - Unit: "MINUTE", - Value: 5, - Remaining: 5, - }, - }, - }, - }, - Absolute: limits.Absolute{ - MaxTotalVolumes: 40, - MaxTotalSnapshots: 40, - MaxTotalVolumeGigabytes: 1000, - MaxTotalBackups: 10, - MaxTotalBackupGigabytes: 1000, - TotalVolumesUsed: 1, - TotalGigabytesUsed: 100, - TotalSnapshotsUsed: 1, - TotalBackupsUsed: 1, - TotalBackupGigabytesUsed: 50, - }, -} - -// HandleGetSuccessfully configures the test server to respond to a Get request -// for a limit. -func HandleGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/limits", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) - }) -} diff --git a/openstack/evs/extensions/limits/testing/requests_test.go b/openstack/evs/extensions/limits/testing/requests_test.go deleted file mode 100644 index b1a59673c..000000000 --- a/openstack/evs/extensions/limits/testing/requests_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/limits" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleGetSuccessfully(t) - - actual, err := limits.Get(client.ServiceClient()).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &LimitsResult, actual) -} diff --git a/openstack/evs/extensions/limits/urls.go b/openstack/evs/extensions/limits/urls.go index edd97e4e0..268f791d1 100644 --- a/openstack/evs/extensions/limits/urls.go +++ b/openstack/evs/extensions/limits/urls.go @@ -1,11 +1,11 @@ package limits import ( - "github.com/gophercloud/gophercloud" + "github.com/opentelekomcloud/gophertelekomcloud" ) const resourcePath = "limits" -func getURL(c *gophercloud.ServiceClient) string { +func getURL(c *golangsdk.ServiceClient) string { return c.ServiceURL(resourcePath) } diff --git a/openstack/evs/extensions/quotasets/doc.go b/openstack/evs/extensions/quotasets/doc.go index a60f953d0..fc3912239 100644 --- a/openstack/evs/extensions/quotasets/doc.go +++ b/openstack/evs/extensions/quotasets/doc.go @@ -22,7 +22,7 @@ Example to Get Quota Set Usage Example to Update a Quota Set updateOpts := quotasets.UpdateOpts{ - Volumes: gophercloud.IntToPointer(100), + Volumes: golangsdk.IntToPointer(100), } quotaset, err := quotasets.Update(blockStorageClient, "project-id", updateOpts).Extract() @@ -35,11 +35,11 @@ Example to Update a Quota Set Example to Update a Quota set with volume_type quotas updateOpts := quotasets.UpdateOpts{ - Volumes: gophercloud.IntToPointer(100), + Volumes: golangsdk.IntToPointer(100), Extra: map[string]interface{}{ - "gigabytes_foo": gophercloud.IntToPointer(100), - "snapshots_foo": gophercloud.IntToPointer(10), - "volumes_foo": gophercloud.IntToPointer(10), + "gigabytes_foo": golangsdk.IntToPointer(100), + "snapshots_foo": golangsdk.IntToPointer(10), + "volumes_foo": golangsdk.IntToPointer(10), }, } diff --git a/openstack/evs/extensions/quotasets/requests.go b/openstack/evs/extensions/quotasets/requests.go index 5cf28aee9..b4209933e 100644 --- a/openstack/evs/extensions/quotasets/requests.go +++ b/openstack/evs/extensions/quotasets/requests.go @@ -3,43 +3,43 @@ package quotasets import ( "fmt" - "github.com/gophercloud/gophercloud" + "github.com/opentelekomcloud/gophertelekomcloud" ) // Get returns public data about a previously created QuotaSet. -func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { +func Get(client *golangsdk.ServiceClient, projectID string) (r GetResult) { resp, err := client.Get(getURL(client, projectID), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // GetDefaults returns public data about the project's default block storage quotas. -func GetDefaults(client *gophercloud.ServiceClient, projectID string) (r GetResult) { +func GetDefaults(client *golangsdk.ServiceClient, projectID string) (r GetResult) { resp, err := client.Get(getDefaultsURL(client, projectID), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // GetUsage returns detailed public data about a previously created QuotaSet. -func GetUsage(client *gophercloud.ServiceClient, projectID string) (r GetUsageResult) { +func GetUsage(client *golangsdk.ServiceClient, projectID string) (r GetUsageResult) { u := fmt.Sprintf("%s?usage=true", getURL(client, projectID)) resp, err := client.Get(u, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Updates the quotas for the given projectID and returns the new QuotaSet. -func Update(client *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(client *golangsdk.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToBlockStorageQuotaUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, projectID), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -53,7 +53,7 @@ type UpdateOptsBuilder interface { // ToBlockStorageQuotaUpdateMap builds the update options into a serializable // format. func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "quota_set") + b, err := golangsdk.BuildRequestBody(opts, "quota_set") if err != nil { return nil, err } @@ -107,10 +107,10 @@ type UpdateOpts struct { } // Resets the quotas for the given tenant to their default values. -func Delete(client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { - resp, err := client.Delete(updateURL(client, projectID), &gophercloud.RequestOpts{ +func Delete(client *golangsdk.ServiceClient, projectID string) (r DeleteResult) { + resp, err := client.Delete(updateURL(client, projectID), &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } diff --git a/openstack/evs/extensions/quotasets/results.go b/openstack/evs/extensions/quotasets/results.go index bc516be5d..2849d4c05 100644 --- a/openstack/evs/extensions/quotasets/results.go +++ b/openstack/evs/extensions/quotasets/results.go @@ -3,8 +3,8 @@ package quotasets import ( "encoding/json" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // QuotaSet is a set of operational limits that allow for control of block @@ -62,7 +62,7 @@ func (r *QuotaSet) UnmarshalJSON(b []byte) error { return err } if resultMap, ok := result.(map[string]interface{}); ok { - r.Extra = gophercloud.RemainingKeys(QuotaSet{}, resultMap) + r.Extra = golangsdk.RemainingKeys(QuotaSet{}, resultMap) } return err @@ -153,7 +153,7 @@ func ExtractQuotaSets(r pagination.Page) ([]QuotaSet, error) { } type quotaResult struct { - gophercloud.Result + golangsdk.Result } // Extract is a method that attempts to interpret any QuotaSet resource response @@ -179,7 +179,7 @@ type UpdateResult struct { } type quotaUsageResult struct { - gophercloud.Result + golangsdk.Result } // GetUsageResult is the response from a Get operation. Call its Extract @@ -201,5 +201,5 @@ func (r quotaUsageResult) Extract() (QuotaUsageSet, error) { // DeleteResult is the response from a Delete operation. Call its ExtractErr // method to determine if the request succeeded or failed. type DeleteResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } diff --git a/openstack/evs/extensions/quotasets/testing/doc.go b/openstack/evs/extensions/quotasets/testing/doc.go deleted file mode 100644 index 30d864eb9..000000000 --- a/openstack/evs/extensions/quotasets/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// quotasets unit tests -package testing diff --git a/openstack/evs/extensions/quotasets/testing/fixtures.go b/openstack/evs/extensions/quotasets/testing/fixtures.go deleted file mode 100644 index 8c9f9318f..000000000 --- a/openstack/evs/extensions/quotasets/testing/fixtures.go +++ /dev/null @@ -1,179 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -const FirstTenantID = "555544443333222211110000ffffeeee" - -var getExpectedJSONBody = ` -{ - "quota_set" : { - "volumes" : 8, - "snapshots" : 9, - "gigabytes" : 10, - "per_volume_gigabytes" : 11, - "backups" : 12, - "backup_gigabytes" : 13, - "groups": 14 - } -}` - -var getExpectedQuotaSet = quotasets.QuotaSet{ - Volumes: 8, - Snapshots: 9, - Gigabytes: 10, - PerVolumeGigabytes: 11, - Backups: 12, - BackupGigabytes: 13, - Groups: 14, - Extra: make(map[string]interface{}), -} - -var getUsageExpectedJSONBody = ` -{ - "quota_set" : { - "id": "555544443333222211110000ffffeeee", - "volumes" : { - "in_use": 15, - "limit": 16, - "reserved": 17 - }, - "snapshots" : { - "in_use": 18, - "limit": 19, - "reserved": 20 - }, - "gigabytes" : { - "in_use": 21, - "limit": 22, - "reserved": 23 - }, - "per_volume_gigabytes" : { - "in_use": 24, - "limit": 25, - "reserved": 26 - }, - "backups" : { - "in_use": 27, - "limit": 28, - "reserved": 29 - }, - "backup_gigabytes" : { - "in_use": 30, - "limit": 31, - "reserved": 32 - }, - "groups" : { - "in_use": 40, - "limit": 41, - "reserved": 42 - } - } -}` - -var getUsageExpectedQuotaSet = quotasets.QuotaUsageSet{ - ID: FirstTenantID, - Volumes: quotasets.QuotaUsage{InUse: 15, Limit: 16, Reserved: 17}, - Snapshots: quotasets.QuotaUsage{InUse: 18, Limit: 19, Reserved: 20}, - Gigabytes: quotasets.QuotaUsage{InUse: 21, Limit: 22, Reserved: 23}, - PerVolumeGigabytes: quotasets.QuotaUsage{InUse: 24, Limit: 25, Reserved: 26}, - Backups: quotasets.QuotaUsage{InUse: 27, Limit: 28, Reserved: 29}, - BackupGigabytes: quotasets.QuotaUsage{InUse: 30, Limit: 31, Reserved: 32}, - Groups: quotasets.QuotaUsage{InUse: 40, Limit: 41, Reserved: 42}, -} - -var fullUpdateExpectedJSONBody = ` -{ - "quota_set": { - "volumes": 8, - "snapshots": 9, - "gigabytes": 10, - "per_volume_gigabytes": 11, - "backups": 12, - "backup_gigabytes": 13, - "groups": 14 - } -}` - -var fullUpdateOpts = quotasets.UpdateOpts{ - Volumes: gophercloud.IntToPointer(8), - Snapshots: gophercloud.IntToPointer(9), - Gigabytes: gophercloud.IntToPointer(10), - PerVolumeGigabytes: gophercloud.IntToPointer(11), - Backups: gophercloud.IntToPointer(12), - BackupGigabytes: gophercloud.IntToPointer(13), - Groups: gophercloud.IntToPointer(14), - Extra: make(map[string]interface{}), -} - -var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ - Volumes: 8, - Snapshots: 9, - Gigabytes: 10, - PerVolumeGigabytes: 11, - Backups: 12, - BackupGigabytes: 13, - Groups: 14, - Extra: make(map[string]interface{}), -} - -var partialUpdateExpectedJSONBody = ` -{ - "quota_set": { - "volumes": 200, - "snapshots": 0, - "gigabytes": 0, - "per_volume_gigabytes": 0, - "backups": 0, - "backup_gigabytes": 0 - } -}` - -var partialUpdateOpts = quotasets.UpdateOpts{ - Volumes: gophercloud.IntToPointer(200), - Snapshots: gophercloud.IntToPointer(0), - Gigabytes: gophercloud.IntToPointer(0), - PerVolumeGigabytes: gophercloud.IntToPointer(0), - Backups: gophercloud.IntToPointer(0), - BackupGigabytes: gophercloud.IntToPointer(0), - Extra: make(map[string]interface{}), -} - -var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{ - Volumes: 200, - Extra: make(map[string]interface{}), -} - -// HandleSuccessfulRequest configures the test server to respond to an HTTP request. -func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput string, uriQueryParams map[string]string) { - - th.Mux.HandleFunc(uriPath, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, httpMethod) - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - w.Header().Add("Content-Type", "application/json") - - if uriQueryParams != nil { - th.TestFormValues(t, r, uriQueryParams) - } - - fmt.Fprintf(w, jsonOutput) - }) -} - -// HandleDeleteSuccessfully tests quotaset deletion. -func HandleDeleteSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.WriteHeader(http.StatusOK) - }) -} diff --git a/openstack/evs/extensions/quotasets/testing/requests_test.go b/openstack/evs/extensions/quotasets/testing/requests_test.go deleted file mode 100644 index 922b8d336..000000000 --- a/openstack/evs/extensions/quotasets/testing/requests_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package testing - -import ( - "errors" - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - uriQueryParms := map[string]string{} - HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &getExpectedQuotaSet, actual) -} - -func TestGetUsage(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - uriQueryParms := map[string]string{"usage": "true"} - HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getUsageExpectedJSONBody, uriQueryParms) - actual, err := quotasets.GetUsage(client.ServiceClient(), FirstTenantID).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, getUsageExpectedQuotaSet, actual) -} - -func TestFullUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - uriQueryParms := map[string]string{} - HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, fullUpdateExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, fullUpdateOpts).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &fullUpdateExpectedQuotaSet, actual) -} - -func TestPartialUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - uriQueryParms := map[string]string{} - HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, partialUpdateExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, partialUpdateOpts).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &partiualUpdateExpectedQuotaSet, actual) -} - -type ErrorUpdateOpts quotasets.UpdateOpts - -func (opts ErrorUpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { - return nil, errors.New("This is an error") -} - -func TestErrorInToBlockStorageQuotaUpdateMap(t *testing.T) { - opts := &ErrorUpdateOpts{} - th.SetupHTTP() - defer th.TeardownHTTP() - HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, "", nil) - _, err := quotasets.Update(client.ServiceClient(), FirstTenantID, opts).Extract() - if err == nil { - t.Fatal("Error handling failed") - } -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleDeleteSuccessfully(t) - - err := quotasets.Delete(client.ServiceClient(), FirstTenantID).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/evs/extensions/quotasets/urls.go b/openstack/evs/extensions/quotasets/urls.go index 7d8e5ceb7..31c87e175 100644 --- a/openstack/evs/extensions/quotasets/urls.go +++ b/openstack/evs/extensions/quotasets/urls.go @@ -1,21 +1,21 @@ package quotasets -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" const resourcePath = "os-quota-sets" -func getURL(c *gophercloud.ServiceClient, projectID string) string { +func getURL(c *golangsdk.ServiceClient, projectID string) string { return c.ServiceURL(resourcePath, projectID) } -func getDefaultsURL(c *gophercloud.ServiceClient, projectID string) string { +func getDefaultsURL(c *golangsdk.ServiceClient, projectID string) string { return c.ServiceURL(resourcePath, projectID, "defaults") } -func updateURL(c *gophercloud.ServiceClient, projectID string) string { +func updateURL(c *golangsdk.ServiceClient, projectID string) string { return getURL(c, projectID) } -func deleteURL(c *gophercloud.ServiceClient, projectID string) string { +func deleteURL(c *golangsdk.ServiceClient, projectID string) string { return getURL(c, projectID) } diff --git a/openstack/evs/extensions/schedulerhints/requests.go b/openstack/evs/extensions/schedulerhints/requests.go index 05b722da4..806768c5c 100644 --- a/openstack/evs/extensions/schedulerhints/requests.go +++ b/openstack/evs/extensions/schedulerhints/requests.go @@ -3,7 +3,7 @@ package schedulerhints import ( "regexp" - "github.com/gophercloud/gophercloud" + "github.com/opentelekomcloud/gophertelekomcloud" ) // SchedulerHints represents a set of scheduling hints that are passed to the @@ -47,7 +47,7 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa if len(opts.DifferentHost) > 0 { for _, diffHost := range opts.DifferentHost { if !uuidRegex.MatchString(diffHost) { - err := gophercloud.ErrInvalidInput{} + err := golangsdk.ErrInvalidInput{} err.Argument = "schedulerhints.SchedulerHints.DifferentHost" err.Value = opts.DifferentHost err.Info = "The hosts must be in UUID format." @@ -60,7 +60,7 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa if len(opts.SameHost) > 0 { for _, sameHost := range opts.SameHost { if !uuidRegex.MatchString(sameHost) { - err := gophercloud.ErrInvalidInput{} + err := golangsdk.ErrInvalidInput{} err.Argument = "schedulerhints.SchedulerHints.SameHost" err.Value = opts.SameHost err.Info = "The hosts must be in UUID format." @@ -72,7 +72,7 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa if opts.LocalToInstance != "" { if !uuidRegex.MatchString(opts.LocalToInstance) { - err := gophercloud.ErrInvalidInput{} + err := golangsdk.ErrInvalidInput{} err.Argument = "schedulerhints.SchedulerHints.LocalToInstance" err.Value = opts.LocalToInstance err.Info = "The instance must be in UUID format." diff --git a/openstack/evs/extensions/schedulerhints/testing/doc.go b/openstack/evs/extensions/schedulerhints/testing/doc.go deleted file mode 100644 index 1915aef2f..000000000 --- a/openstack/evs/extensions/schedulerhints/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// schedulerhints unit tests -package testing diff --git a/openstack/evs/extensions/schedulerhints/testing/requests_test.go b/openstack/evs/extensions/schedulerhints/testing/requests_test.go deleted file mode 100644 index 2ba27c7ef..000000000 --- a/openstack/evs/extensions/schedulerhints/testing/requests_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerhints" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" - th "github.com/gophercloud/gophercloud/testhelper" -) - -func TestCreateOpts(t *testing.T) { - - base := volumes.CreateOpts{ - Size: 10, - Name: "testvolume", - } - schedulerHints := schedulerhints.SchedulerHints{ - DifferentHost: []string{ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287", - }, - SameHost: []string{ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287", - }, - LocalToInstance: "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", - AdditionalProperties: map[string]interface{}{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, - } - - ext := schedulerhints.CreateOptsExt{ - VolumeCreateOptsBuilder: base, - SchedulerHints: schedulerHints, - } - - expected := ` - { - "volume": { - "size": 10, - "name": "testvolume" - }, - "OS-SCH-HNT:scheduler_hints": { - "different_host": [ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287" - ], - "same_host": [ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287" - ], - "local_to_instance": "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", - "mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" - } - } - ` - actual, err := ext.ToVolumeCreateMap() - th.AssertNoErr(t, err) - th.CheckJSONEquals(t, expected, actual) -} diff --git a/openstack/evs/extensions/schedulerstats/requests.go b/openstack/evs/extensions/schedulerstats/requests.go index 7b374dcd8..08d079be5 100644 --- a/openstack/evs/extensions/schedulerstats/requests.go +++ b/openstack/evs/extensions/schedulerstats/requests.go @@ -1,8 +1,8 @@ package schedulerstats import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the @@ -23,12 +23,12 @@ type ListOpts struct { // ToStoragePoolsListQuery formats a ListOpts into a query string. func (opts ListOpts) ToStoragePoolsListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list storage pool information. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { +func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := storagePoolsListURL(client) if opts != nil { query, err := opts.ToStoragePoolsListQuery() diff --git a/openstack/evs/extensions/schedulerstats/results.go b/openstack/evs/extensions/schedulerstats/results.go index 672990713..745e888e7 100644 --- a/openstack/evs/extensions/schedulerstats/results.go +++ b/openstack/evs/extensions/schedulerstats/results.go @@ -5,7 +5,7 @@ import ( "math" "strconv" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Capabilities represents the information of an individual StoragePool. diff --git a/openstack/evs/extensions/schedulerstats/testing/fixtures.go b/openstack/evs/extensions/schedulerstats/testing/fixtures.go deleted file mode 100644 index 3af0718a4..000000000 --- a/openstack/evs/extensions/schedulerstats/testing/fixtures.go +++ /dev/null @@ -1,110 +0,0 @@ -package testing - -import ( - "fmt" - "math" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -const StoragePoolsListBody = ` -{ - "pools": [ - { - "name": "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd" - }, - { - "name": "rbd:cinder.volumes.hdd@cinder.volumes#cinder.volumes.hdd" - } - ] -} -` - -const StoragePoolsListBodyDetail = ` -{ - "pools": [ - { - "capabilities": { - "driver_version": "1.2.0", - "filter_function": null, - "free_capacity_gb": 64765, - "goodness_function": null, - "max_over_subscription_ratio": "1.5", - "multiattach": false, - "reserved_percentage": 0, - "storage_protocol": "ceph", - "timestamp": "2016-11-24T10:33:51.248360", - "total_capacity_gb": 787947.93, - "vendor_name": "Open Source", - "volume_backend_name": "cinder.volumes.ssd" - }, - "name": "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd" - }, - { - "capabilities": { - "driver_version": "1.2.0", - "filter_function": null, - "free_capacity_gb": "unknown", - "goodness_function": null, - "max_over_subscription_ratio": 1.5, - "multiattach": false, - "reserved_percentage": 0, - "storage_protocol": "ceph", - "timestamp": "2016-11-24T10:33:43.138628", - "total_capacity_gb": "infinite", - "vendor_name": "Open Source", - "volume_backend_name": "cinder.volumes.hdd" - }, - "name": "rbd:cinder.volumes.hdd@cinder.volumes.hdd#cinder.volumes.hdd" - } - ] -} -` - -var ( - StoragePoolFake1 = schedulerstats.StoragePool{ - Name: "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd", - Capabilities: schedulerstats.Capabilities{ - DriverVersion: "1.2.0", - FreeCapacityGB: 64765, - MaxOverSubscriptionRatio: "1.5", - StorageProtocol: "ceph", - TotalCapacityGB: 787947.93, - VendorName: "Open Source", - VolumeBackendName: "cinder.volumes.ssd", - }, - } - - StoragePoolFake2 = schedulerstats.StoragePool{ - Name: "rbd:cinder.volumes.hdd@cinder.volumes.hdd#cinder.volumes.hdd", - Capabilities: schedulerstats.Capabilities{ - DriverVersion: "1.2.0", - FreeCapacityGB: 0.0, - MaxOverSubscriptionRatio: "1.5", - StorageProtocol: "ceph", - TotalCapacityGB: math.Inf(1), - VendorName: "Open Source", - VolumeBackendName: "cinder.volumes.hdd", - }, - } -) - -func HandleStoragePoolsListSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/scheduler-stats/get_pools", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - - r.ParseForm() - if r.FormValue("detail") == "true" { - fmt.Fprintf(w, StoragePoolsListBodyDetail) - } else { - fmt.Fprintf(w, StoragePoolsListBody) - } - }) -} diff --git a/openstack/evs/extensions/schedulerstats/testing/requests_test.go b/openstack/evs/extensions/schedulerstats/testing/requests_test.go deleted file mode 100644 index 8a4ef5180..000000000 --- a/openstack/evs/extensions/schedulerstats/testing/requests_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" - "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestListStoragePoolsDetail(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() - HandleStoragePoolsListSuccessfully(t) - - pages := 0 - err := schedulerstats.List(client.ServiceClient(), schedulerstats.ListOpts{Detail: true}).EachPage(func(page pagination.Page) (bool, error) { - pages++ - - actual, err := schedulerstats.ExtractStoragePools(page) - testhelper.AssertNoErr(t, err) - - if len(actual) != 2 { - t.Fatalf("Expected 2 backends, got %d", len(actual)) - } - testhelper.CheckDeepEquals(t, StoragePoolFake1, actual[0]) - testhelper.CheckDeepEquals(t, StoragePoolFake2, actual[1]) - - return true, nil - }) - - testhelper.AssertNoErr(t, err) - - if pages != 1 { - t.Errorf("Expected 1 page, saw %d", pages) - } -} diff --git a/openstack/evs/extensions/schedulerstats/urls.go b/openstack/evs/extensions/schedulerstats/urls.go index c0ddb3695..3ecd22547 100644 --- a/openstack/evs/extensions/schedulerstats/urls.go +++ b/openstack/evs/extensions/schedulerstats/urls.go @@ -1,7 +1,7 @@ package schedulerstats -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" -func storagePoolsListURL(c *gophercloud.ServiceClient) string { +func storagePoolsListURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("scheduler-stats", "get_pools") } diff --git a/openstack/evs/extensions/services/requests.go b/openstack/evs/extensions/services/requests.go index 0edcfc9d7..84c7de2a2 100644 --- a/openstack/evs/extensions/services/requests.go +++ b/openstack/evs/extensions/services/requests.go @@ -1,8 +1,8 @@ package services import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the List @@ -22,12 +22,12 @@ type ListOpts struct { // ToServiceListQuery formats a ListOpts into a query string. func (opts ListOpts) ToServiceListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list services. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { +func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToServiceListQuery() diff --git a/openstack/evs/extensions/services/results.go b/openstack/evs/extensions/services/results.go index 49ad48ef6..338a257cf 100644 --- a/openstack/evs/extensions/services/results.go +++ b/openstack/evs/extensions/services/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Service represents a Blockstorage service in the OpenStack cloud. @@ -51,7 +51,7 @@ func (r *Service) UnmarshalJSON(b []byte) error { type tmp Service var s struct { tmp - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { diff --git a/openstack/evs/extensions/services/testing/fixtures.go b/openstack/evs/extensions/services/testing/fixtures.go deleted file mode 100644 index 9d14723c1..000000000 --- a/openstack/evs/extensions/services/testing/fixtures.go +++ /dev/null @@ -1,97 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -// ServiceListBody is sample response to the List call -const ServiceListBody = ` -{ - "services": [{ - "status": "enabled", - "binary": "cinder-scheduler", - "zone": "nova", - "state": "up", - "updated_at": "2017-06-29T05:50:35.000000", - "host": "devstack", - "disabled_reason": null - }, - { - "status": "enabled", - "binary": "cinder-backup", - "zone": "nova", - "state": "up", - "updated_at": "2017-06-29T05:50:42.000000", - "host": "devstack", - "disabled_reason": null - }, - { - "status": "enabled", - "binary": "cinder-volume", - "zone": "nova", - "frozen": false, - "state": "up", - "updated_at": "2017-06-29T05:50:39.000000", - "cluster": null, - "host": "devstack@lvmdriver-1", - "replication_status": "disabled", - "active_backend_id": null, - "disabled_reason": null - }] -} -` - -// First service from the ServiceListBody -var FirstFakeService = services.Service{ - Binary: "cinder-scheduler", - DisabledReason: "", - Host: "devstack", - State: "up", - Status: "enabled", - UpdatedAt: time.Date(2017, 6, 29, 5, 50, 35, 0, time.UTC), - Zone: "nova", -} - -// Second service from the ServiceListBody -var SecondFakeService = services.Service{ - Binary: "cinder-backup", - DisabledReason: "", - Host: "devstack", - State: "up", - Status: "enabled", - UpdatedAt: time.Date(2017, 6, 29, 5, 50, 42, 0, time.UTC), - Zone: "nova", -} - -// Third service from the ServiceListBody -var ThirdFakeService = services.Service{ - ActiveBackendID: "", - Binary: "cinder-volume", - Cluster: "", - DisabledReason: "", - Frozen: false, - Host: "devstack@lvmdriver-1", - ReplicationStatus: "disabled", - State: "up", - Status: "enabled", - UpdatedAt: time.Date(2017, 6, 29, 5, 50, 39, 0, time.UTC), - Zone: "nova", -} - -// HandleListSuccessfully configures the test server to respond to a List request. -func HandleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-services", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ServiceListBody) - }) -} diff --git a/openstack/evs/extensions/services/testing/requests_test.go b/openstack/evs/extensions/services/testing/requests_test.go deleted file mode 100644 index 4178c2369..000000000 --- a/openstack/evs/extensions/services/testing/requests_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" - "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestListServices(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() - HandleListSuccessfully(t) - - pages := 0 - err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - pages++ - - actual, err := services.ExtractServices(page) - if err != nil { - return false, err - } - - if len(actual) != 3 { - t.Fatalf("Expected 3 services, got %d", len(actual)) - } - testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) - testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) - testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) - - return true, nil - }) - - testhelper.AssertNoErr(t, err) - - if pages != 1 { - t.Errorf("Expected 1 page, saw %d", pages) - } -} diff --git a/openstack/evs/extensions/services/urls.go b/openstack/evs/extensions/services/urls.go index 61d794007..a718789cd 100644 --- a/openstack/evs/extensions/services/urls.go +++ b/openstack/evs/extensions/services/urls.go @@ -1,7 +1,7 @@ package services -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" -func listURL(c *gophercloud.ServiceClient) string { +func listURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("os-services") } diff --git a/openstack/evs/extensions/volumeactions/doc.go b/openstack/evs/extensions/volumeactions/doc.go index 34db834f7..c83b0997a 100644 --- a/openstack/evs/extensions/volumeactions/doc.go +++ b/openstack/evs/extensions/volumeactions/doc.go @@ -56,7 +56,7 @@ Example of Initializing a Volume Connection IP: "127.0.0.1", Host: "stack", Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: gophercloud.Disabled, + Multipath: golangsdk.Disabled, Platform: "x86_64", OSType: "linux2", } @@ -72,7 +72,7 @@ Example of Initializing a Volume Connection IP: "127.0.0.1", Host: "stack", Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: gophercloud.Disabled, + Multipath: golangsdk.Disabled, Platform: "x86_64", OSType: "linux2", } diff --git a/openstack/evs/extensions/volumeactions/requests.go b/openstack/evs/extensions/volumeactions/requests.go index 09dfb9ed2..39918656a 100644 --- a/openstack/evs/extensions/volumeactions/requests.go +++ b/openstack/evs/extensions/volumeactions/requests.go @@ -1,7 +1,7 @@ package volumeactions import ( - "github.com/gophercloud/gophercloud" + "github.com/opentelekomcloud/gophertelekomcloud" ) // AttachOptsBuilder allows extensions to add additional parameters to the @@ -37,30 +37,30 @@ type AttachOpts struct { // ToVolumeAttachMap assembles a request body based on the contents of a // AttachOpts. func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-attach") + return golangsdk.BuildRequestBody(opts, "os-attach") } // Attach will attach a volume based on the values in AttachOpts. -func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) { +func Attach(client *golangsdk.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) { b, err := opts.ToVolumeAttachMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // BeginDetaching will mark the volume as detaching. -func BeginDetaching(client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { +func BeginDetaching(client *golangsdk.ServiceClient, id string) (r BeginDetachingResult) { b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -79,40 +79,40 @@ type DetachOpts struct { // ToVolumeDetachMap assembles a request body based on the contents of a // DetachOpts. func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-detach") + return golangsdk.BuildRequestBody(opts, "os-detach") } // Detach will detach a volume based on volume ID. -func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { +func Detach(client *golangsdk.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { b, err := opts.ToVolumeDetachMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Reserve will reserve a volume based on volume ID. -func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) { +func Reserve(client *golangsdk.ServiceClient, id string) (r ReserveResult) { b := map[string]interface{}{"os-reserve": make(map[string]interface{})} - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Unreserve will unreserve a volume based on volume ID. -func Unreserve(client *gophercloud.ServiceClient, id string) (r UnreserveResult) { +func Unreserve(client *golangsdk.ServiceClient, id string) (r UnreserveResult) { b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -139,21 +139,21 @@ type InitializeConnectionOpts struct { // ToVolumeInitializeConnectionMap assembles a request body based on the contents of a // InitializeConnectionOpts. func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "connector") + b, err := golangsdk.BuildRequestBody(opts, "connector") return map[string]interface{}{"os-initialize_connection": b}, err } // InitializeConnection initializes an iSCSI connection by volume ID. -func InitializeConnection(client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { +func InitializeConnection(client *golangsdk.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { b, err := opts.ToVolumeInitializeConnectionMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -178,21 +178,21 @@ type TerminateConnectionOpts struct { // ToVolumeTerminateConnectionMap assembles a request body based on the contents of a // TerminateConnectionOpts. func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "connector") + b, err := golangsdk.BuildRequestBody(opts, "connector") return map[string]interface{}{"os-terminate_connection": b}, err } // TerminateConnection terminates an iSCSI connection by volume ID. -func TerminateConnection(client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { +func TerminateConnection(client *golangsdk.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { b, err := opts.ToVolumeTerminateConnectionMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -212,21 +212,21 @@ type ExtendSizeOpts struct { // ToVolumeExtendSizeMap assembles a request body based on the contents of an // ExtendSizeOpts. func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-extend") + return golangsdk.BuildRequestBody(opts, "os-extend") } // ExtendSize will extend the size of the volume based on the provided information. // This operation does not return a response body. -func ExtendSize(client *gophercloud.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) { +func ExtendSize(client *golangsdk.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) { b, err := opts.ToVolumeExtendSizeMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -262,27 +262,27 @@ type UploadImageOpts struct { // ToVolumeUploadImageMap assembles a request body based on the contents of a // UploadImageOpts. func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-volume_upload_image") + return golangsdk.BuildRequestBody(opts, "os-volume_upload_image") } // UploadImage will upload an image based on the values in UploadImageOptsBuilder. -func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { +func UploadImage(client *golangsdk.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { b, err := opts.ToVolumeUploadImageMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // ForceDelete will delete the volume regardless of state. -func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { +func ForceDelete(client *golangsdk.ServiceClient, id string) (r ForceDeleteResult) { resp, err := client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -301,20 +301,20 @@ type ImageMetadataOpts struct { // ToImageMetadataMap assembles a request body based on the contents of a // ImageMetadataOpts. func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-set_image_metadata") + return golangsdk.BuildRequestBody(opts, "os-set_image_metadata") } // SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder. -func SetImageMetadata(client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { +func SetImageMetadata(client *golangsdk.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { b, err := opts.ToImageMetadataMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -327,20 +327,20 @@ type BootableOpts struct { // ToBootableMap assembles a request body based on the contents of a // BootableOpts. func (opts BootableOpts) ToBootableMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-set_bootable") + return golangsdk.BuildRequestBody(opts, "os-set_bootable") } // SetBootable will set bootable status on a volume based on the values in BootableOpts -func SetBootable(client *gophercloud.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { +func SetBootable(client *golangsdk.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { b, err := opts.ToBootableMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -374,21 +374,21 @@ type ChangeTypeOpts struct { // ToVolumeChangeTypeMap assembles a request body based on the contents of an // ChangeTypeOpts. func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-retype") + return golangsdk.BuildRequestBody(opts, "os-retype") } // ChangeType will change the volume type of the volume based on the provided information. // This operation does not return a response body. -func ChangeType(client *gophercloud.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) { +func ChangeType(client *golangsdk.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) { b, err := opts.ToVolumeChangeTypeMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -402,19 +402,19 @@ type ReImageOpts struct { // ToReImageMap assembles a request body based on the contents of a ReImageOpts. func (opts ReImageOpts) ToReImageMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-reimage") + return golangsdk.BuildRequestBody(opts, "os-reimage") } // ReImage will re-image a volume based on the values in ReImageOpts -func ReImage(client *gophercloud.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) { +func ReImage(client *golangsdk.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) { b, err := opts.ToReImageMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } diff --git a/openstack/evs/extensions/volumeactions/results.go b/openstack/evs/extensions/volumeactions/results.go index 95b5bac1c..fff8d58f7 100644 --- a/openstack/evs/extensions/volumeactions/results.go +++ b/openstack/evs/extensions/volumeactions/results.go @@ -4,69 +4,69 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" + "github.com/opentelekomcloud/gophertelekomcloud" ) // AttachResult contains the response body and error from an Attach request. type AttachResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // BeginDetachingResult contains the response body and error from a BeginDetach // request. type BeginDetachingResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // DetachResult contains the response body and error from a Detach request. type DetachResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // UploadImageResult contains the response body and error from an UploadImage // request. type UploadImageResult struct { - gophercloud.Result + golangsdk.Result } // SetImageMetadataResult contains the response body and error from an SetImageMetadata // request. type SetImageMetadataResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // SetBootableResult contains the response body and error from a SetBootable // request. type SetBootableResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // ReserveResult contains the response body and error from a Reserve request. type ReserveResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // UnreserveResult contains the response body and error from an Unreserve // request. type UnreserveResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // TerminateConnectionResult contains the response body and error from a // TerminateConnection request. type TerminateConnectionResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // InitializeConnectionResult contains the response body and error from an // InitializeConnection request. type InitializeConnectionResult struct { - gophercloud.Result + golangsdk.Result } // ExtendSizeResult contains the response body and error from an ExtendSize request. type ExtendSizeResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // Extract will get the connection information out of the @@ -120,9 +120,9 @@ func (r *ImageVolumeType) UnmarshalJSON(b []byte) error { type tmp ImageVolumeType var s struct { tmp - CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` - DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at"` + CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` + DeletedAt golangsdk.JSONRFC3339MilliNoZ `json:"deleted_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -182,7 +182,7 @@ func (r *VolumeImage) UnmarshalJSON(b []byte) error { type tmp VolumeImage var s struct { tmp - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -207,15 +207,15 @@ func (r UploadImageResult) Extract() (VolumeImage, error) { // ForceDeleteResult contains the response body and error from a ForceDelete request. type ForceDeleteResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // ChangeTypeResult contains the response body and error from an ChangeType request. type ChangeTypeResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // ReImageResult contains the response body and error from a ReImage request. type ReImageResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } diff --git a/openstack/evs/extensions/volumeactions/testing/doc.go b/openstack/evs/extensions/volumeactions/testing/doc.go deleted file mode 100644 index 336406df1..000000000 --- a/openstack/evs/extensions/volumeactions/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// volumeactions unit tests -package testing diff --git a/openstack/evs/extensions/volumeactions/testing/fixtures.go b/openstack/evs/extensions/volumeactions/testing/fixtures.go deleted file mode 100644 index 378a120bc..000000000 --- a/openstack/evs/extensions/volumeactions/testing/fixtures.go +++ /dev/null @@ -1,372 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -func MockAttachResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", - 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, ` -{ - "os-attach": - { - "mountpoint": "/mnt", - "mode": "rw", - "instance_uuid": "50902f4f-a974-46a0-85e9-7efc5e22dfdd" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockBeginDetachingResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", - 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, ` -{ - "os-begin_detaching": {} -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockDetachResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", - 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, ` -{ - "os-detach": {} -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockUploadImageResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", - 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, ` -{ - "os-volume_upload_image": { - "container_format": "bare", - "force": true, - "image_name": "test", - "disk_format": "raw" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, ` -{ - "os-volume_upload_image": { - "container_format": "bare", - "display_description": null, - "id": "cd281d77-8217-4830-be95-9528227c105c", - "image_id": "ecb92d98-de08-45db-8235-bbafe317269c", - "image_name": "test", - "disk_format": "raw", - "size": 5, - "status": "uploading", - "updated_at": "2017-07-17T09:29:22.000000", - "volume_type": { - "created_at": "2016-05-04T08:54:14.000000", - "deleted": false, - "deleted_at": null, - "description": null, - "extra_specs": { - "volume_backend_name": "basic.ru-2a" - }, - "id": "b7133444-62f6-4433-8da3-70ac332229b7", - "is_public": true, - "name": "basic.ru-2a", - "updated_at": "2016-05-04T09:15:33.000000" - } - } -} - `) - }) -} - -func MockReserveResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", - 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, ` -{ - "os-reserve": {} -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockUnreserveResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", - 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, ` -{ - "os-unreserve": {} -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockInitializeConnectionResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", - 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, ` -{ - "os-initialize_connection": - { - "connector": - { - "ip":"127.0.0.1", - "host":"stack", - "initiator":"iqn.1994-05.com.redhat:17cf566367d2", - "multipath": false, - "platform": "x86_64", - "os_type": "linux2" - } - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{ -"connection_info": { - "data": { - "target_portals": [ - "172.31.17.48:3260" - ], - "auth_method": "CHAP", - "auth_username": "5MLtcsTEmNN5jFVcT6ui", - "access_mode": "rw", - "target_lun": 0, - "volume_id": "cd281d77-8217-4830-be95-9528227c105c", - "target_luns": [ - 0 - ], - "target_iqns": [ - "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c" - ], - "auth_password": "x854ZY5Re3aCkdNL", - "target_discovered": false, - "encrypted": false, - "qos_specs": null, - "target_iqn": "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c", - "target_portal": "172.31.17.48:3260" - }, - "driver_volume_type": "iscsi" - } - }`) - }) -} - -func MockTerminateConnectionResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", - 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, ` -{ - "os-terminate_connection": - { - "connector": - { - "ip":"127.0.0.1", - "host":"stack", - "initiator":"iqn.1994-05.com.redhat:17cf566367d2", - "multipath": true, - "platform": "x86_64", - "os_type": "linux2" - } - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockExtendSizeResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", - 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, ` -{ - "os-extend": - { - "new_size": 3 - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockForceDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestBody(t, r, `{"os-force_delete":""}`) - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockSetImageMetadataResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", 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, ` -{ - "os-set_image_metadata": { - "metadata": { - "label": "test" - } - } -} - `) - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockSetBootableResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", 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, ` -{ - "os-set_bootable": { - "bootable": true - } -} - `) - w.Header().Add("Content-Type", "application/json") - w.Header().Add("Content-Length", "0") - w.WriteHeader(http.StatusOK) - }) -} - -func MockReImageResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", 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, ` -{ - "os-reimage": { - "image_id": "71543ced-a8af-45b6-a5c4-a46282108a90", - "reimage_reserved": false - } -} - `) - w.Header().Add("Content-Type", "application/json") - w.Header().Add("Content-Length", "0") - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockChangeTypeResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", - 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, ` -{ - "os-retype": - { - "new_type": "ssd", - "migration_policy": "on-demand" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} diff --git a/openstack/evs/extensions/volumeactions/testing/requests_test.go b/openstack/evs/extensions/volumeactions/testing/requests_test.go deleted file mode 100644 index 2191a8a78..000000000 --- a/openstack/evs/extensions/volumeactions/testing/requests_test.go +++ /dev/null @@ -1,226 +0,0 @@ -package testing - -import ( - "testing" - "time" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestAttach(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockAttachResponse(t) - - options := &volumeactions.AttachOpts{ - MountPoint: "/mnt", - Mode: "rw", - InstanceUUID: "50902f4f-a974-46a0-85e9-7efc5e22dfdd", - } - err := volumeactions.Attach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestBeginDetaching(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockBeginDetachingResponse(t) - - err := volumeactions.BeginDetaching(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() - th.AssertNoErr(t, err) -} - -func TestDetach(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDetachResponse(t) - - err := volumeactions.Detach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", &volumeactions.DetachOpts{}).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestUploadImage(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - MockUploadImageResponse(t) - options := &volumeactions.UploadImageOpts{ - ContainerFormat: "bare", - DiskFormat: "raw", - ImageName: "test", - Force: true, - } - - actual, err := volumeactions.UploadImage(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() - th.AssertNoErr(t, err) - - expected := volumeactions.VolumeImage{ - VolumeID: "cd281d77-8217-4830-be95-9528227c105c", - ContainerFormat: "bare", - DiskFormat: "raw", - Description: "", - ImageID: "ecb92d98-de08-45db-8235-bbafe317269c", - ImageName: "test", - Size: 5, - Status: "uploading", - UpdatedAt: time.Date(2017, 7, 17, 9, 29, 22, 0, time.UTC), - VolumeType: volumeactions.ImageVolumeType{ - ID: "b7133444-62f6-4433-8da3-70ac332229b7", - Name: "basic.ru-2a", - Description: "", - IsPublic: true, - ExtraSpecs: map[string]interface{}{"volume_backend_name": "basic.ru-2a"}, - QosSpecsID: "", - Deleted: false, - DeletedAt: time.Time{}, - CreatedAt: time.Date(2016, 5, 4, 8, 54, 14, 0, time.UTC), - UpdatedAt: time.Date(2016, 5, 4, 9, 15, 33, 0, time.UTC), - }, - } - th.AssertDeepEquals(t, expected, actual) -} - -func TestReserve(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockReserveResponse(t) - - err := volumeactions.Reserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() - th.AssertNoErr(t, err) -} - -func TestUnreserve(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUnreserveResponse(t) - - err := volumeactions.Unreserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() - th.AssertNoErr(t, err) -} - -func TestInitializeConnection(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockInitializeConnectionResponse(t) - - options := &volumeactions.InitializeConnectionOpts{ - IP: "127.0.0.1", - Host: "stack", - Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: gophercloud.Disabled, - Platform: "x86_64", - OSType: "linux2", - } - _, err := volumeactions.InitializeConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() - th.AssertNoErr(t, err) -} - -func TestTerminateConnection(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockTerminateConnectionResponse(t) - - options := &volumeactions.TerminateConnectionOpts{ - IP: "127.0.0.1", - Host: "stack", - Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: gophercloud.Enabled, - Platform: "x86_64", - OSType: "linux2", - } - err := volumeactions.TerminateConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestExtendSize(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockExtendSizeResponse(t) - - options := &volumeactions.ExtendSizeOpts{ - NewSize: 3, - } - - err := volumeactions.ExtendSize(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestForceDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockForceDeleteResponse(t) - - res := volumeactions.ForceDelete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} - -func TestSetImageMetadata(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockSetImageMetadataResponse(t) - - options := &volumeactions.ImageMetadataOpts{ - Metadata: map[string]string{ - "label": "test", - }, - } - - err := volumeactions.SetImageMetadata(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestSetBootable(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockSetBootableResponse(t) - - options := volumeactions.BootableOpts{ - Bootable: true, - } - - err := volumeactions.SetBootable(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestReImage(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockReImageResponse(t) - - options := volumeactions.ReImageOpts{ - ImageID: "71543ced-a8af-45b6-a5c4-a46282108a90", - ReImageReserved: false, - } - - err := volumeactions.ReImage(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestChangeType(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockChangeTypeResponse(t) - - options := &volumeactions.ChangeTypeOpts{ - NewType: "ssd", - MigrationPolicy: "on-demand", - } - - err := volumeactions.ChangeType(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/evs/extensions/volumeactions/urls.go b/openstack/evs/extensions/volumeactions/urls.go index 20486ed71..fe44e072c 100644 --- a/openstack/evs/extensions/volumeactions/urls.go +++ b/openstack/evs/extensions/volumeactions/urls.go @@ -1,7 +1,7 @@ package volumeactions -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" -func actionURL(c *gophercloud.ServiceClient, id string) string { +func actionURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("volumes", id, "action") } diff --git a/openstack/evs/extensions/volumetransfers/requests.go b/openstack/evs/extensions/volumetransfers/requests.go index d32ac70f8..71c05507b 100644 --- a/openstack/evs/extensions/volumetransfers/requests.go +++ b/openstack/evs/extensions/volumetransfers/requests.go @@ -1,8 +1,8 @@ package volumetransfers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // CreateOpts contains options for a Volume transfer. @@ -17,20 +17,20 @@ type CreateOpts struct { // ToCreateMap assembles a request body based on the contents of a // TransferOpts. func (opts CreateOpts) ToCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "transfer") + return golangsdk.BuildRequestBody(opts, "transfer") } // Create will create a volume tranfer request based on the values in CreateOpts. -func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { +func Create(client *golangsdk.ServiceClient, opts CreateOpts) (r CreateResult) { b, err := opts.ToCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(transferURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(transferURL(client), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -43,27 +43,27 @@ type AcceptOpts struct { // ToAcceptMap assembles a request body based on the contents of a // AcceptOpts. func (opts AcceptOpts) ToAcceptMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "accept") + return golangsdk.BuildRequestBody(opts, "accept") } // Accept will accept a volume tranfer request based on the values in AcceptOpts. -func Accept(client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r CreateResult) { +func Accept(client *golangsdk.ServiceClient, id string, opts AcceptOpts) (r CreateResult) { b, err := opts.ToAcceptMap() if err != nil { r.Err = err return } - resp, err := client.Post(acceptURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(acceptURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Delete deletes a volume transfer. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { +func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -95,12 +95,12 @@ type ListOpts struct { // ToTransferListQuery formats a ListOpts into a query string. func (opts ListOpts) ToTransferListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // List returns Transfers optionally limited by the conditions provided in ListOpts. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { +func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToTransferListQuery() @@ -117,8 +117,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves the Transfer with the provided ID. To extract the Transfer object // from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { +func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } diff --git a/openstack/evs/extensions/volumetransfers/results.go b/openstack/evs/extensions/volumetransfers/results.go index 3217174a8..e5f7dc9ec 100644 --- a/openstack/evs/extensions/volumetransfers/results.go +++ b/openstack/evs/extensions/volumetransfers/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Transfer represents a Volume Transfer record @@ -23,7 +23,7 @@ func (r *Transfer) UnmarshalJSON(b []byte) error { type tmp Transfer var s struct { tmp - CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -37,7 +37,7 @@ func (r *Transfer) UnmarshalJSON(b []byte) error { } type commonResult struct { - gophercloud.Result + golangsdk.Result } // Extract will get the Transfer object out of the commonResult object. @@ -64,7 +64,7 @@ type GetResult struct { // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // ExtractTransfers extracts and returns Transfers. It is used while iterating over a transfers.List call. @@ -92,11 +92,11 @@ func (r TransferPage) IsEmpty() (bool, error) { func (page TransferPage) NextPageURL() (string, error) { var s struct { - Links []gophercloud.Link `json:"transfers_links"` + Links []golangsdk.Link `json:"transfers_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } - return gophercloud.ExtractNextURL(s.Links) + return golangsdk.ExtractNextURL(s.Links) } diff --git a/openstack/evs/extensions/volumetransfers/testing/fixtures.go b/openstack/evs/extensions/volumetransfers/testing/fixtures.go deleted file mode 100644 index 9714323f9..000000000 --- a/openstack/evs/extensions/volumetransfers/testing/fixtures.go +++ /dev/null @@ -1,216 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetransfers" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -const ListOutput = ` -{ - "transfers": [ - { - "created_at": "2020-02-28T12:44:28.051989", - "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", - "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "links": [ - { - "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "self" - }, - { - "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "bookmark" - } - ], - "name": null - } - ] -} -` - -const GetOutput = ` -{ - "transfer": { - "created_at": "2020-02-28T12:44:28.051989", - "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", - "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "links": [ - { - "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "self" - }, - { - "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "bookmark" - } - ], - "name": null - } -} -` - -const CreateRequest = ` -{ - "transfer": { - "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" - } -} -` - -const CreateResponse = ` -{ - "transfer": { - "auth_key": "cb67e0e7387d9eac", - "created_at": "2020-02-28T12:44:28.051989", - "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "links": [ - { - "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "self" - }, - { - "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "bookmark" - } - ], - "name": null, - "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" - } -} -` - -const AcceptTransferRequest = ` -{ - "accept": { - "auth_key": "9266c59563c84664" - } -} -` - -const AcceptTransferResponse = ` -{ - "transfer": { - "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "links": [ - { - "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "self" - }, - { - "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "bookmark" - } - ], - "name": null, - "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" - } -} -` - -var TransferRequest = volumetransfers.CreateOpts{ - VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", -} - -var createdAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-02-28T12:44:28.051989") -var TransferResponse = volumetransfers.Transfer{ - ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - AuthKey: "cb67e0e7387d9eac", - Name: "", - VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", - CreatedAt: createdAt, - Links: []map[string]string{ - { - "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "self", - }, - { - "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "bookmark", - }, - }, -} - -var TransferListResponse = []volumetransfers.Transfer{TransferResponse} - -var AcceptRequest = volumetransfers.AcceptOpts{ - AuthKey: "9266c59563c84664", -} - -var AcceptResponse = volumetransfers.Transfer{ - ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - Name: "", - VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", - Links: []map[string]string{ - { - "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "self", - }, - { - "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", - "rel": "bookmark", - }, - }, -} - -func HandleCreateTransfer(t *testing.T) { - th.Mux.HandleFunc("/os-volume-transfer", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - w.Header().Add("Content-Type", "application/json") - th.TestJSONRequest(t, r, CreateRequest) - - w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, CreateResponse) - }) -} - -func HandleAcceptTransfer(t *testing.T) { - th.Mux.HandleFunc("/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f/accept", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - w.Header().Add("Content-Type", "application/json") - th.TestJSONRequest(t, r, AcceptTransferRequest) - - w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, AcceptTransferResponse) - }) -} - -func HandleDeleteTransfer(t *testing.T) { - th.Mux.HandleFunc("/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.WriteHeader(http.StatusNoContent) - }) -} - -func HandleListTransfers(t *testing.T) { - th.Mux.HandleFunc("/os-volume-transfer/detail", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - w.Header().Add("Content-Type", "application/json") - th.TestFormValues(t, r, map[string]string{"all_tenants": "true"}) - - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) - }) -} - -func HandleGetTransfer(t *testing.T) { - th.Mux.HandleFunc("/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - w.Header().Add("Content-Type", "application/json") - - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) - }) -} diff --git a/openstack/evs/extensions/volumetransfers/testing/requests_test.go b/openstack/evs/extensions/volumetransfers/testing/requests_test.go deleted file mode 100644 index 85dc35962..000000000 --- a/openstack/evs/extensions/volumetransfers/testing/requests_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetransfers" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestCreateTransfer(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleCreateTransfer(t) - - actual, err := volumetransfers.Create(client.ServiceClient(), TransferRequest).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, TransferResponse, *actual) -} - -func TestAcceptTransfer(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleAcceptTransfer(t) - - actual, err := volumetransfers.Accept(client.ServiceClient(), TransferResponse.ID, AcceptRequest).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, AcceptResponse, *actual) -} - -func TestDeleteTransfer(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleDeleteTransfer(t) - - err := volumetransfers.Delete(client.ServiceClient(), TransferResponse.ID).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestListTransfers(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleListTransfers(t) - - expectedResponse := TransferListResponse - expectedResponse[0].AuthKey = "" - - count := 0 - err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).EachPage(func(page pagination.Page) (bool, error) { - count++ - - actual, err := volumetransfers.ExtractTransfers(page) - th.AssertNoErr(t, err) - - th.CheckDeepEquals(t, expectedResponse, actual) - - return true, nil - }) - th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) -} - -func TestListTransfersAllPages(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleListTransfers(t) - - expectedResponse := TransferListResponse - expectedResponse[0].AuthKey = "" - - allPages, err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).AllPages() - th.AssertNoErr(t, err) - actual, err := volumetransfers.ExtractTransfers(allPages) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, expectedResponse, actual) -} - -func TestGetTransfer(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleGetTransfer(t) - - expectedResponse := TransferResponse - expectedResponse.AuthKey = "" - - actual, err := volumetransfers.Get(client.ServiceClient(), TransferResponse.ID).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, expectedResponse, *actual) -} diff --git a/openstack/evs/extensions/volumetransfers/urls.go b/openstack/evs/extensions/volumetransfers/urls.go index cf06dc494..472d12d9f 100644 --- a/openstack/evs/extensions/volumetransfers/urls.go +++ b/openstack/evs/extensions/volumetransfers/urls.go @@ -1,23 +1,23 @@ package volumetransfers -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" -func transferURL(c *gophercloud.ServiceClient) string { +func transferURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("os-volume-transfer") } -func acceptURL(c *gophercloud.ServiceClient, id string) string { +func acceptURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("os-volume-transfer", id, "accept") } -func deleteURL(c *gophercloud.ServiceClient, id string) string { +func deleteURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("os-volume-transfer", id) } -func listURL(c *gophercloud.ServiceClient) string { +func listURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("os-volume-transfer", "detail") } -func getURL(c *gophercloud.ServiceClient, id string) string { +func getURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("os-volume-transfer", id) } diff --git a/openstack/evs/v1/apiversions/get.go b/openstack/evs/v1/apiversions/get.go deleted file mode 100644 index 519e0194e..000000000 --- a/openstack/evs/v1/apiversions/get.go +++ /dev/null @@ -1,19 +0,0 @@ -package apiversions - -import ( - "strings" - - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" -) - -func Get(client *golangsdk.ServiceClient, v string) (*APIVersion, error) { - raw, err := client.Get(client.ServiceURL(strings.TrimRight(v, "/")+"/"), nil, nil) - if err != nil { - return nil, err - } - - var res APIVersion - err = extract.IntoStructPtr(raw.Body, &res, "version") - return &res, err -} diff --git a/openstack/evs/v1/apiversions/list.go b/openstack/evs/v1/apiversions/list.go deleted file mode 100644 index 67ae617cd..000000000 --- a/openstack/evs/v1/apiversions/list.go +++ /dev/null @@ -1,26 +0,0 @@ -package apiversions - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" -) - -func List(client *golangsdk.ServiceClient) ([]APIVersion, error) { - raw, err := client.Get(client.ServiceURL(""), nil, nil) - if err != nil { - return nil, err - } - - var res []APIVersion - err = extract.IntoSlicePtr(raw.Body, &res, "versions") - return res, err -} - -type APIVersion struct { - // unique identifier - ID string `json:"id"` - // current status e.g. SUPPORTED - Status string `json:"status"` - // date last updated e.g. 2014-06-28T12:20:21Z - Updated string `json:"updated"` -} diff --git a/openstack/evs/v1/apiversions/testing/fixtures.go b/openstack/evs/v1/apiversions/testing/fixtures.go deleted file mode 100644 index 8264796db..000000000 --- a/openstack/evs/v1/apiversions/testing/fixtures.go +++ /dev/null @@ -1,91 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - _, _ = fmt.Fprint(w, `{ - "versions": [ - { - "status": "CURRENT", - "updated": "2012-01-04T11:33:21Z", - "id": "v1.0", - "links": [ - { - "href": "http://23.253.228.211:8776/v1/", - "rel": "self" - } - ] - }, - { - "status": "CURRENT", - "updated": "2012-11-21T11:33:21Z", - "id": "v2.0", - "links": [ - { - "href": "http://23.253.228.211:8776/v2/", - "rel": "self" - } - ] - } - ] - }`) - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/v1/", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - _, _ = fmt.Fprint(w, `{ - "version": { - "status": "CURRENT", - "updated": "2012-01-04T11:33:21Z", - "media-types": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.volume+xml;version=1" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.volume+json;version=1" - } - ], - "id": "v1.0", - "links": [ - { - "href": "http://23.253.228.211:8776/v1/", - "rel": "self" - }, - { - "href": "http://jorgew.github.com/block-storage-api/content/os-block-storage-1.0.pdf", - "type": "application/pdf", - "rel": "describedby" - }, - { - "href": "http://docs.rackspacecloud.com/servers/api/v1.1/application.wadl", - "type": "application/vnd.sun.wadl+xml", - "rel": "describedby" - } - ] - } - }`) - }) -} diff --git a/openstack/evs/v1/apiversions/testing/requests_test.go b/openstack/evs/v1/apiversions/testing/requests_test.go deleted file mode 100644 index f8d15d4cd..000000000 --- a/openstack/evs/v1/apiversions/testing/requests_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v1/apiversions" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -func TestListVersions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - actual, err := apiversions.List(client.ServiceClient()) - th.AssertNoErr(t, err) - - expected := []apiversions.APIVersion{ - { - ID: "v1.0", - Status: "CURRENT", - Updated: "2012-01-04T11:33:21Z", - }, - { - ID: "v2.0", - Status: "CURRENT", - Updated: "2012-11-21T11:33:21Z", - }, - } - - th.AssertDeepEquals(t, expected, actual) -} - -func TestAPIInfo(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - actual, err := apiversions.Get(client.ServiceClient(), "v1") - th.AssertNoErr(t, err) - - expected := apiversions.APIVersion{ - ID: "v1.0", - Status: "CURRENT", - Updated: "2012-01-04T11:33:21Z", - } - - th.AssertEquals(t, actual.ID, expected.ID) - th.AssertEquals(t, actual.Status, expected.Status) - th.AssertEquals(t, actual.Updated, expected.Updated) -} diff --git a/openstack/evs/v3/attachments/requests.go b/openstack/evs/v3/attachments/requests.go index b6032a3b0..712fa1a61 100644 --- a/openstack/evs/v3/attachments/requests.go +++ b/openstack/evs/v3/attachments/requests.go @@ -1,8 +1,8 @@ package attachments import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the @@ -34,39 +34,39 @@ type CreateOpts struct { // ToAttachmentCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToAttachmentCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "attachment") + return golangsdk.BuildRequestBody(opts, "attachment") } // Create will create a new Attachment based on the values in CreateOpts. To // extract the Attachment object from the response, call the Extract method on // the CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToAttachmentCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200, 202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Delete will delete the existing Attachment with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), &gophercloud.RequestOpts{ +func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(deleteURL(client, id), &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Get retrieves the Attachment with the provided ID. To extract the Attachment // object from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { +func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -110,13 +110,13 @@ type ListOpts struct { // ToAttachmentListQuery formats a ListOpts into a query string. func (opts ListOpts) ToAttachmentListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // List returns Attachments optionally limited by the conditions provided in // ListOpts. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { +func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToAttachmentListQuery() @@ -147,34 +147,34 @@ type UpdateOpts struct { // ToAttachmentUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToAttachmentUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "attachment") + return golangsdk.BuildRequestBody(opts, "attachment") } // Update will update the Attachment with provided information. To extract the // updated Attachment from the response, call the Extract method on the // UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToAttachmentUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Complete will complete an attachment for a cinder volume. // Available starting in the 3.44 microversion. -func Complete(client *gophercloud.ServiceClient, id string) (r CompleteResult) { +func Complete(client *golangsdk.ServiceClient, id string) (r CompleteResult) { b := map[string]interface{}{ "os-complete": nil, } - resp, err := client.Post(completeURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(completeURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{204}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } diff --git a/openstack/evs/v3/attachments/results.go b/openstack/evs/v3/attachments/results.go index 0a97730ed..45618a3cb 100644 --- a/openstack/evs/v3/attachments/results.go +++ b/openstack/evs/v3/attachments/results.go @@ -6,8 +6,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Attachment contains all the information associated with an OpenStack @@ -37,8 +37,8 @@ func (r *Attachment) UnmarshalJSON(b []byte) error { type tmp Attachment var s struct { tmp - AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` - DetachedAt gophercloud.JSONRFC3339MilliNoZ `json:"detached_at"` + AttachedAt golangsdk.JSONRFC3339MilliNoZ `json:"attached_at"` + DetachedAt golangsdk.JSONRFC3339MilliNoZ `json:"detached_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -73,7 +73,7 @@ func ExtractAttachments(r pagination.Page) ([]Attachment, error) { } type commonResult struct { - gophercloud.Result + golangsdk.Result } // Extract will get the Attachment object out of the commonResult object. @@ -111,10 +111,10 @@ type UpdateResult struct { // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // CompleteResult contains the response body and error from a Complete request. type CompleteResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } diff --git a/openstack/evs/v3/attachments/testing/fixtures.go b/openstack/evs/v3/attachments/testing/fixtures.go deleted file mode 100644 index b19245e57..000000000 --- a/openstack/evs/v3/attachments/testing/fixtures.go +++ /dev/null @@ -1,233 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -var ( - attachedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2015-09-16T09:28:52.000000") - detachedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2015-09-16T09:28:52.000000") - expectedAttachment = &attachments.Attachment{ - ID: "05551600-a936-4d4a-ba42-79a037c1-c91a", - VolumeID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Instance: "83ec2e3b-4321-422b-8706-a84185f52a0a", - AttachMode: "rw", - Status: "attaching", - AttachedAt: attachedAt, - DetachedAt: detachedAt, - ConnectionInfo: map[string]interface{}{}, - } -) - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/attachments/detail", 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) - - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, ` - { - "attachments": [ - { - "status": "attaching", - "detached_at": "2015-09-16T09:28:52.000000", - "connection_info": {}, - "attached_at": "2015-09-16T09:28:52.000000", - "attach_mode": "rw", - "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", - "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" - } - ], - "attachments_links": [ - { - "href": "%s/attachments/detail?marker=1", - "rel": "next" - } - ] -} - `, th.Server.URL) - case "1": - fmt.Fprintf(w, `{"volumes": []}`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", 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, ` -{ - "attachment": { - "status": "attaching", - "detached_at": "2015-09-16T09:28:52.000000", - "connection_info": {}, - "attached_at": "2015-09-16T09:28:52.000000", - "attach_mode": "rw", - "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", - "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" - } -} - `) - }) -} - -func MockCreateResponse(t *testing.T) { - th.Mux.HandleFunc("/attachments", 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, ` -{ - "attachment": { - "instance_uuid": "83ec2e3b-4321-422b-8706-a84185f52a0a", - "connector": { - "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", - "ip": "192.168.1.20", - "platform": "x86_64", - "host": "tempest-1", - "os_type": "linux2", - "multipath": false, - "mountpoint": "/dev/vdb", - "mode": "rw" - }, - "volume_uuid": "289da7f8-6440-407c-9fb4-7db01ec49164" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "attachment": { - "status": "attaching", - "detached_at": "2015-09-16T09:28:52.000000", - "connection_info": {}, - "attached_at": "2015-09-16T09:28:52.000000", - "attach_mode": "rw", - "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", - "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" - } -} - `) - }) -} - -func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusOK) - }) -} - -func MockUpdateResponse(t *testing.T) { - th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - th.TestJSONRequest(t, r, ` -{ - "attachment": { - "connector": { - "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", - "ip": "192.168.1.20", - "platform": "x86_64", - "host": "tempest-1", - "os_type": "linux2", - "multipath": false, - "mountpoint": "/dev/vdb", - "mode": "rw" - } - } -} - `) - - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "attachment": { - "status": "attaching", - "detached_at": "2015-09-16T09:28:52.000000", - "connection_info": {}, - "attached_at": "2015-09-16T09:28:52.000000", - "attach_mode": "rw", - "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", - "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" - } -} - `) - }) -} - -func MockUpdateEmptyResponse(t *testing.T) { - th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - th.TestJSONRequest(t, r, ` -{ - "attachment": { - "connector": null - } -} - `) - - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "attachment": { - "status": "attaching", - "detached_at": "2015-09-16T09:28:52.000000", - "connection_info": {}, - "attached_at": "2015-09-16T09:28:52.000000", - "attach_mode": "rw", - "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", - "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" - } -} - `) - }) -} - -var completeRequest = ` -{ - "os-complete": null -} -` - -func MockCompleteResponse(t *testing.T) { - th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestJSONRequest(t, r, completeRequest) - w.WriteHeader(http.StatusNoContent) - }) -} diff --git a/openstack/evs/v3/attachments/testing/requests_test.go b/openstack/evs/v3/attachments/testing/requests_test.go deleted file mode 100644 index 2c9793100..000000000 --- a/openstack/evs/v3/attachments/testing/requests_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestListAll(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - allPages, err := attachments.List(client.ServiceClient(), &attachments.ListOpts{}).AllPages() - th.AssertNoErr(t, err) - actual, err := attachments.ExtractAttachments(allPages) - th.AssertNoErr(t, err) - - expected := []attachments.Attachment{*expectedAttachment} - - th.CheckDeepEquals(t, expected, actual) - -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - attachment, err := attachments.Get(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").Extract() - th.AssertNoErr(t, err) - - th.AssertDeepEquals(t, expectedAttachment, attachment) -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - options := &attachments.CreateOpts{ - InstanceUUID: "83ec2e3b-4321-422b-8706-a84185f52a0a", - Connector: map[string]interface{}{ - "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", - "ip": "192.168.1.20", - "platform": "x86_64", - "host": "tempest-1", - "os_type": "linux2", - "multipath": false, - "mountpoint": "/dev/vdb", - "mode": "rw", - }, - VolumeUUID: "289da7f8-6440-407c-9fb4-7db01ec49164", - } - attachment, err := attachments.Create(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertDeepEquals(t, expectedAttachment, attachment) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := attachments.Delete(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a") - th.AssertNoErr(t, res.Err) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateResponse(t) - - options := &attachments.UpdateOpts{ - Connector: map[string]interface{}{ - "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", - "ip": "192.168.1.20", - "platform": "x86_64", - "host": "tempest-1", - "os_type": "linux2", - "multipath": false, - "mountpoint": "/dev/vdb", - "mode": "rw", - }, - } - attachment, err := attachments.Update(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expectedAttachment, attachment) -} - -func TestUpdateEmpty(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateEmptyResponse(t) - - options := attachments.UpdateOpts{} - attachment, err := attachments.Update(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expectedAttachment, attachment) -} - -func TestComplete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCompleteResponse(t) - - err := attachments.Complete(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/evs/v3/attachments/urls.go b/openstack/evs/v3/attachments/urls.go index a774d7772..dfa2b986f 100644 --- a/openstack/evs/v3/attachments/urls.go +++ b/openstack/evs/v3/attachments/urls.go @@ -1,27 +1,27 @@ package attachments -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" -func createURL(c *gophercloud.ServiceClient) string { +func createURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("attachments") } -func listURL(c *gophercloud.ServiceClient) string { +func listURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("attachments", "detail") } -func getURL(c *gophercloud.ServiceClient, id string) string { +func getURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("attachments", id) } -func updateURL(c *gophercloud.ServiceClient, id string) string { +func updateURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("attachments", id) } -func deleteURL(c *gophercloud.ServiceClient, id string) string { +func deleteURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("attachments", id) } -func completeURL(c *gophercloud.ServiceClient, id string) string { +func completeURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("attachments", id, "action") } diff --git a/openstack/evs/v3/attachments/util.go b/openstack/evs/v3/attachments/util.go index dca1b888e..4f7b9dfab 100644 --- a/openstack/evs/v3/attachments/util.go +++ b/openstack/evs/v3/attachments/util.go @@ -1,13 +1,13 @@ package attachments import ( - "github.com/gophercloud/gophercloud" + "github.com/opentelekomcloud/gophertelekomcloud" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { + return golangsdk.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() if err != nil { return false, err diff --git a/openstack/evs/v3/qos/requests.go b/openstack/evs/v3/qos/requests.go index 8169f8219..10e1933f8 100644 --- a/openstack/evs/v3/qos/requests.go +++ b/openstack/evs/v3/qos/requests.go @@ -1,8 +1,8 @@ package qos import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) type CreateOptsBuilder interface { @@ -39,7 +39,7 @@ type CreateOpts struct { // ToQoSCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToQoSCreateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "qos_specs") + b, err := golangsdk.BuildRequestBody(opts, "qos_specs") if err != nil { return nil, err } @@ -58,16 +58,16 @@ func (opts CreateOpts) ToQoSCreateMap() (map[string]interface{}, error) { // Create will create a new QoS based on the values in CreateOpts. To extract // the QoS object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToQoSCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -86,12 +86,12 @@ type DeleteOpts struct { // ToQoSDeleteQuery formats a DeleteOpts into a query string. func (opts DeleteOpts) ToQoSDeleteQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // Delete will delete the existing QoS with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { +func Delete(client *golangsdk.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { url := deleteURL(client, id) if opts != nil { query, err := opts.ToQoSDeleteQuery() @@ -102,14 +102,14 @@ func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder url += query } resp, err := client.Delete(url, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } type ListOpts struct { // Sort is Comma-separated list of sort keys and optional sort // directions in the form of < key > [: < direction > ]. A valid - //direction is asc (ascending) or desc (descending). + // direction is asc (ascending) or desc (descending). Sort string `q:"sort"` // Marker and Limit control paging. @@ -123,14 +123,14 @@ type ListOpts struct { // ToQoSListQuery formats a ListOpts into a query string. func (opts ListOpts) ToQoSListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // List instructs OpenStack to provide a list of QoS. // You may provide criteria by which List curtails its results for easier // processing. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { +func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToQoSListQuery() @@ -146,11 +146,11 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details of a single qos. Use Extract to convert its // result into a QoS. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(getURL(client, id), &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -178,7 +178,7 @@ type UpdateOptsBuilder interface { // ToQoSUpdateMap assembles a request body based on the contents of a // UpdateOpts. func (opts UpdateOpts) ToQoSUpdateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "qos_specs") + b, err := golangsdk.BuildRequestBody(opts, "qos_specs") if err != nil { return nil, err } @@ -197,16 +197,16 @@ func (opts UpdateOpts) ToQoSUpdateMap() (map[string]interface{}, error) { // Update will update an existing QoS based on the values in UpdateOpts. // To extract the QoS object from the response, call the Extract method // on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r updateResult) { +func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r updateResult) { b, err := opts.ToQoSUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -226,16 +226,16 @@ func (opts DeleteKeysOpts) ToDeleteKeysCreateMap() (map[string]interface{}, erro } // DeleteKeys will delete the keys/specs from the specified QoS -func DeleteKeys(client *gophercloud.ServiceClient, qosID string, opts DeleteKeysOptsBuilder) (r DeleteResult) { +func DeleteKeys(client *golangsdk.ServiceClient, qosID string, opts DeleteKeysOptsBuilder) (r DeleteResult) { b, err := opts.ToDeleteKeysCreateMap() if err != nil { r.Err = err return } - resp, err := client.Put(deleteKeysURL(client, qosID), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(deleteKeysURL(client, qosID), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -253,12 +253,12 @@ type AssociateOpts struct { // ToQosAssociateQuery formats an AssociateOpts into a query string func (opts AssociateOpts) ToQosAssociateQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // Associate will associate a qos with a volute type -func Associate(client *gophercloud.ServiceClient, qosID string, opts AssociateOptsBuilder) (r AssociateResult) { +func Associate(client *golangsdk.ServiceClient, qosID string, opts AssociateOptsBuilder) (r AssociateResult) { url := associateURL(client, qosID) query, err := opts.ToQosAssociateQuery() if err != nil { @@ -267,10 +267,10 @@ func Associate(client *gophercloud.ServiceClient, qosID string, opts AssociateOp } url += query - resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + resp, err := client.Get(url, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -288,12 +288,12 @@ type DisassociateOpts struct { // ToQosDisassociateQuery formats a DisassociateOpts into a query string func (opts DisassociateOpts) ToQosDisassociateQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // Disassociate will disassociate a qos from a volute type -func Disassociate(client *gophercloud.ServiceClient, qosID string, opts DisassociateOptsBuilder) (r DisassociateResult) { +func Disassociate(client *golangsdk.ServiceClient, qosID string, opts DisassociateOptsBuilder) (r DisassociateResult) { url := disassociateURL(client, qosID) query, err := opts.ToQosDisassociateQuery() if err != nil { @@ -302,24 +302,24 @@ func Disassociate(client *gophercloud.ServiceClient, qosID string, opts Disassoc } url += query - resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + resp, err := client.Get(url, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // DisassociateAll will disassociate a qos from all volute types -func DisassociateAll(client *gophercloud.ServiceClient, qosID string) (r DisassociateAllResult) { - resp, err := client.Get(disassociateAllURL(client, qosID), nil, &gophercloud.RequestOpts{ +func DisassociateAll(client *golangsdk.ServiceClient, qosID string) (r DisassociateAllResult) { + resp, err := client.Get(disassociateAllURL(client, qosID), nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // ListAssociations retrieves the associations of a QoS. -func ListAssociations(client *gophercloud.ServiceClient, qosID string) pagination.Pager { +func ListAssociations(client *golangsdk.ServiceClient, qosID string) pagination.Pager { url := listAssociationsURL(client, qosID) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { diff --git a/openstack/evs/v3/qos/results.go b/openstack/evs/v3/qos/results.go index 34008aed8..93abf3b97 100644 --- a/openstack/evs/v3/qos/results.go +++ b/openstack/evs/v3/qos/results.go @@ -1,8 +1,8 @@ package qos import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // QoS contains all the information associated with an OpenStack QoS specification. @@ -18,7 +18,7 @@ type QoS struct { } type commonResult struct { - gophercloud.Result + golangsdk.Result } // Extract will get the QoS object out of the commonResult object. @@ -40,7 +40,7 @@ type CreateResult struct { // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } type QoSPage struct { @@ -57,13 +57,13 @@ func (page QoSPage) IsEmpty() (bool, error) { // next page of results. func (page QoSPage) NextPageURL() (string, error) { var s struct { - Links []gophercloud.Link `json:"qos_specs_links"` + Links []golangsdk.Link `json:"qos_specs_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } - return gophercloud.ExtractNextURL(s.Links) + return golangsdk.ExtractNextURL(s.Links) } // ExtractQoS provides access to the list of qos in a page acquired @@ -95,22 +95,22 @@ func (r updateResult) Extract() (map[string]string, error) { // key-value pairs. Call its Extract method to interpret it as a // map[string]interface. type updateResult struct { - gophercloud.Result + golangsdk.Result } // AssociateResult contains the response body and error from a Associate request. type AssociateResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // DisassociateResult contains the response body and error from a Disassociate request. type DisassociateResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // DisassociateAllResult contains the response body and error from a DisassociateAll request. type DisassociateAllResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // QoS contains all the information associated with an OpenStack QoS specification. diff --git a/openstack/evs/v3/qos/testing/doc.go b/openstack/evs/v3/qos/testing/doc.go deleted file mode 100644 index 0155a0963..000000000 --- a/openstack/evs/v3/qos/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package testing for qos_v3 -package testing diff --git a/openstack/evs/v3/qos/testing/fixtures.go b/openstack/evs/v3/qos/testing/fixtures.go deleted file mode 100644 index d3bf00d69..000000000 --- a/openstack/evs/v3/qos/testing/fixtures.go +++ /dev/null @@ -1,232 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -var createQoSExpected = qos.QoS{ - ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", - Name: "qos-001", - Consumer: "front-end", - Specs: map[string]string{ - "read_iops_sec": "20000", - }, -} - -var getQoSExpected = qos.QoS{ - ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", - Name: "qos-001", - Consumer: "front-end", - Specs: map[string]string{ - "read_iops_sec": "20000", - }, -} - -func MockCreateResponse(t *testing.T) { - th.Mux.HandleFunc("/qos-specs", 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, ` -{ - "qos_specs": { - "name": "qos-001", - "consumer": "front-end", - "read_iops_sec": "20000" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "qos_specs": { - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "name": "qos-001", - "consumer": "front-end", - "specs": { - "read_iops_sec": "20000" - } - } -} - `) - }) -} - -func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/qos-specs", 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") - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, ` - { - "qos_specs": [ - { - "consumer": "back-end", - "id": "1", - "name": "foo", - "specs": {} - }, - { - "consumer": "front-end", - "id": "2", - "name": "bar", - "specs" : { - "read_iops_sec" : "20000" - } - } - - ], - "qos_specs_links": [ - { - "href": "%s/qos-specs?marker=2", - "rel": "next" - } - ] - } - `, th.Server.URL) - case "2": - fmt.Fprintf(w, `{ "qos_specs": [] }`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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, ` -{ - "qos_specs": { - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "name": "qos-001", - "consumer": "front-end", - "specs": { - "read_iops_sec": "20000" - } - } -} - `) - }) -} - -// UpdateBody provides a PUT result of the qos_specs for a QoS -const UpdateBody = ` -{ - "qos_specs" : { - "consumer": "back-end", - "read_iops_sec": "40000", - "write_iops_sec": "40000" - } -} -` - -// UpdateQos is the expected qos_specs returned from PUT on a qos's qos_specs -var UpdateQos = map[string]string{ - "consumer": "back-end", - "read_iops_sec": "40000", - "write_iops_sec": "40000", -} - -func MockUpdateResponse(t *testing.T) { - th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestHeader(t, r, "Accept", "application/json") - th.TestJSONRequest(t, r, `{ - "qos_specs": { - "consumer": "back-end", - "read_iops_sec": "40000", - "write_iops_sec": "40000" - } - }`) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateBody) - }) -} - -func MockDeleteKeysResponse(t *testing.T) { - th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/delete_keys", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestJSONRequest(t, r, `{ - "keys": [ - "read_iops_sec" - ] - }`) - - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockAssociateResponse(t *testing.T) { - th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/associate", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockDisassociateResponse(t *testing.T) { - th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/disassociate", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockDisassociateAllResponse(t *testing.T) { - th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/disassociate_all", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockListAssociationsResponse(t *testing.T) { - th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/associations", 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") - fmt.Fprintf(w, ` - { - "qos_associations": [ - { - "name": "foo", - "id": "2f954bcf047c4ee9b09a37d49ae6db54", - "association_type": "volume_type" - } - ] - } - `) - }) -} diff --git a/openstack/evs/v3/qos/testing/requests_test.go b/openstack/evs/v3/qos/testing/requests_test.go deleted file mode 100644 index e452fa2ee..000000000 --- a/openstack/evs/v3/qos/testing/requests_test.go +++ /dev/null @@ -1,178 +0,0 @@ -package testing - -import ( - "reflect" - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - options := qos.CreateOpts{ - Name: "qos-001", - Consumer: qos.ConsumerFront, - Specs: map[string]string{ - "read_iops_sec": "20000", - }, - } - actual, err := qos.Create(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &createQoSExpected, actual) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := qos.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteOpts{}) - th.AssertNoErr(t, res.Err) -} - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - pages := 0 - err := qos.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { - pages++ - actual, err := qos.ExtractQoS(page) - if err != nil { - return false, err - } - - expected := []qos.QoS{ - {ID: "1", Consumer: "back-end", Name: "foo", Specs: map[string]string{}}, - {ID: "2", Consumer: "front-end", Name: "bar", Specs: map[string]string{ - "read_iops_sec": "20000", - }, - }, - } - - if !reflect.DeepEqual(expected, actual) { - t.Errorf("Expected %#v, but was %#v", expected, actual) - } - - return true, nil - }) - if err != nil { - t.Fatal(err) - } - if pages != 1 { - t.Errorf("Expected one page, got %d", pages) - } -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - actual, err := qos.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &getQoSExpected, actual) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - MockUpdateResponse(t) - - updateOpts := qos.UpdateOpts{ - Consumer: qos.ConsumerBack, - Specs: map[string]string{ - "read_iops_sec": "40000", - "write_iops_sec": "40000", - }, - } - - expected := UpdateQos - actual, err := qos.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", updateOpts).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, expected, actual) -} - -func TestDeleteKeys(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteKeysResponse(t) - - res := qos.DeleteKeys(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteKeysOpts{"read_iops_sec"}) - th.AssertNoErr(t, res.Err) -} - -func TestAssociate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockAssociateResponse(t) - - associateOpts := qos.AssociateOpts{ - VolumeTypeID: "b596be6a-0ce9-43fa-804a-5c5e181ede76", - } - - res := qos.Associate(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", associateOpts) - th.AssertNoErr(t, res.Err) -} - -func TestDisssociate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDisassociateResponse(t) - - disassociateOpts := qos.DisassociateOpts{ - VolumeTypeID: "b596be6a-0ce9-43fa-804a-5c5e181ede76", - } - - res := qos.Disassociate(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", disassociateOpts) - th.AssertNoErr(t, res.Err) -} - -func TestDissasociateAll(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDisassociateAllResponse(t) - - res := qos.DisassociateAll(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} - -func TestQosAssociationsList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListAssociationsResponse(t) - - expected := []qos.QosAssociation{ - { - Name: "foo", - ID: "2f954bcf047c4ee9b09a37d49ae6db54", - AssociationType: "volume_type", - }, - } - - allPages, err := qos.ListAssociations(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").AllPages() - th.AssertNoErr(t, err) - - actual, err := qos.ExtractAssociations(allPages) - th.AssertNoErr(t, err) - - if !reflect.DeepEqual(expected, actual) { - t.Errorf("Expected %#v, but was %#v", expected, actual) - } -} diff --git a/openstack/evs/v3/qos/urls.go b/openstack/evs/v3/qos/urls.go index e0e4a0eec..9be3accc3 100644 --- a/openstack/evs/v3/qos/urls.go +++ b/openstack/evs/v3/qos/urls.go @@ -1,43 +1,43 @@ package qos -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" -func getURL(client *gophercloud.ServiceClient, id string) string { +func getURL(client *golangsdk.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id) } -func createURL(c *gophercloud.ServiceClient) string { +func createURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("qos-specs") } -func listURL(c *gophercloud.ServiceClient) string { +func listURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("qos-specs") } -func deleteURL(c *gophercloud.ServiceClient, id string) string { +func deleteURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("qos-specs", id) } -func updateURL(client *gophercloud.ServiceClient, id string) string { +func updateURL(client *golangsdk.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id) } -func deleteKeysURL(client *gophercloud.ServiceClient, id string) string { +func deleteKeysURL(client *golangsdk.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id, "delete_keys") } -func associateURL(client *gophercloud.ServiceClient, id string) string { +func associateURL(client *golangsdk.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id, "associate") } -func disassociateURL(client *gophercloud.ServiceClient, id string) string { +func disassociateURL(client *golangsdk.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id, "disassociate") } -func disassociateAllURL(client *gophercloud.ServiceClient, id string) string { +func disassociateAllURL(client *golangsdk.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id, "disassociate_all") } -func listAssociationsURL(client *gophercloud.ServiceClient, id string) string { +func listAssociationsURL(client *golangsdk.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id, "associations") } diff --git a/openstack/evs/v3/snapshots/requests.go b/openstack/evs/v3/snapshots/requests.go index 7dcfed794..6eab5f71e 100644 --- a/openstack/evs/v3/snapshots/requests.go +++ b/openstack/evs/v3/snapshots/requests.go @@ -1,8 +1,8 @@ package snapshots import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the @@ -25,37 +25,37 @@ type CreateOpts struct { // ToSnapshotCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "snapshot") + return golangsdk.BuildRequestBody(opts, "snapshot") } // Create will create a new Snapshot based on the values in CreateOpts. To // extract the Snapshot object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSnapshotCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Delete will delete the existing Snapshot with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { +func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { +func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -100,13 +100,13 @@ type ListOpts struct { // ToSnapshotListQuery formats a ListOpts into a query string. func (opts ListOpts) ToSnapshotListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // List returns Snapshots optionally limited by the conditions provided in // ListOpts. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { +func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToSnapshotListQuery() @@ -137,21 +137,21 @@ type UpdateOpts struct { // ToSnapshotUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "snapshot") + return golangsdk.BuildRequestBody(opts, "snapshot") } // Update will update the Snapshot with provided information. To extract the updated // Snapshot from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSnapshotUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -171,21 +171,21 @@ type UpdateMetadataOpts struct { // ToSnapshotUpdateMetadataMap assembles a request body based on the contents of // an UpdateMetadataOpts. func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "") + return golangsdk.BuildRequestBody(opts, "") } // UpdateMetadata will update the Snapshot with provided information. To // extract the updated Snapshot from the response, call the ExtractMetadata // method on the UpdateMetadataResult. -func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { +func UpdateMetadata(client *golangsdk.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { b, err := opts.ToSnapshotUpdateMetadataMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } diff --git a/openstack/evs/v3/snapshots/results.go b/openstack/evs/v3/snapshots/results.go index 3c81a447a..deddb4f47 100644 --- a/openstack/evs/v3/snapshots/results.go +++ b/openstack/evs/v3/snapshots/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Snapshot contains all the information associated with a Cinder Snapshot. @@ -50,7 +50,7 @@ type GetResult struct { // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // UpdateResult contains the response body and error from an Update request. @@ -68,8 +68,8 @@ func (r *Snapshot) UnmarshalJSON(b []byte) error { type tmp Snapshot var s struct { tmp - CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -93,13 +93,13 @@ func (r SnapshotPage) IsEmpty() (bool, error) { // next page of results. func (r SnapshotPage) NextPageURL() (string, error) { var s struct { - Links []gophercloud.Link `json:"snapshots_links"` + Links []golangsdk.Link `json:"snapshots_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } - return gophercloud.ExtractNextURL(s.Links) + return golangsdk.ExtractNextURL(s.Links) } // ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. @@ -126,7 +126,7 @@ func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) } type commonResult struct { - gophercloud.Result + golangsdk.Result } // Extract will get the Snapshot object out of the commonResult object. diff --git a/openstack/evs/v3/snapshots/testing/doc.go b/openstack/evs/v3/snapshots/testing/doc.go deleted file mode 100644 index 89a5d0d3e..000000000 --- a/openstack/evs/v3/snapshots/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package testing for snapshots_v3 -package testing diff --git a/openstack/evs/v3/snapshots/testing/fixtures.go b/openstack/evs/v3/snapshots/testing/fixtures.go deleted file mode 100644 index 3ae3bb86d..000000000 --- a/openstack/evs/v3/snapshots/testing/fixtures.go +++ /dev/null @@ -1,178 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -// MockListResponse provides mock responce for list snapshot API call -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots", 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) - - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, ` - { - "snapshots": [ - { - "id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "name": "snapshot-001", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "description": "Daily Backup", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - }, - { - "id": "96c3bda7-c82a-4f50-be73-ca7621794835", - "name": "snapshot-002", - "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", - "description": "Weekly Backup", - "status": "available", - "size": 25, - "created_at": "2017-05-30T03:35:03.000000" - } - ], - "snapshots_links": [ - { - "href": "%s/snapshots?marker=1", - "rel": "next" - }] - } - `, th.Server.URL) - case "1": - fmt.Fprintf(w, `{"snapshots": []}`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -// MockGetResponse provides mock responce for get snapshot API call -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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, ` -{ - "snapshot": { - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "name": "snapshot-001", - "description": "Daily backup", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - } -} - `) - }) -} - -// MockCreateResponse provides mock responce for create snapshot API call -func MockCreateResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots", 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, ` -{ - "snapshot": { - "volume_id": "1234", - "name": "snapshot-001" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, ` -{ - "snapshot": { - "volume_id": "1234", - "name": "snapshot-001", - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "description": "Daily backup", - "volume_id": "1234", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - } -} - `) - }) -} - -// MockUpdateMetadataResponse provides mock responce for update metadata snapshot API call -func MockUpdateMetadataResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots/123/metadata", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestHeader(t, r, "Content-Type", "application/json") - th.TestJSONRequest(t, r, ` - { - "metadata": { - "key": "v1" - } - } - `) - - fmt.Fprintf(w, ` - { - "metadata": { - "key": "v1" - } - } - `) - }) -} - -// MockDeleteResponse provides mock responce for delete snapshot API call -func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusNoContent) - }) -} - -// MockUpdateResponse provides mock responce for update snapshot API call -func MockUpdateResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "snapshot": { - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "name": "snapshot-002", - "description": "Daily backup 002", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000", - "updated_at": "2017-05-30T03:35:03.000000" - } -} - `) - }) -} diff --git a/openstack/evs/v3/snapshots/testing/requests_test.go b/openstack/evs/v3/snapshots/testing/requests_test.go deleted file mode 100644 index a787928ba..000000000 --- a/openstack/evs/v3/snapshots/testing/requests_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package testing - -import ( - "testing" - "time" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - count := 0 - - snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := snapshots.ExtractSnapshots(page) - if err != nil { - t.Errorf("Failed to extract snapshots: %v", err) - return false, err - } - - expected := []snapshots.Snapshot{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "snapshot-001", - VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - Status: "available", - Size: 30, - CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), - Description: "Daily Backup", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "snapshot-002", - VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", - Status: "available", - Size: 25, - CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), - Description: "Weekly Backup", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, v.Name, "snapshot-001") - th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} - n, err := snapshots.Create(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.VolumeID, "1234") - th.AssertEquals(t, n.Name, "snapshot-001") - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestUpdateMetadata(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateMetadataResponse(t) - - expected := map[string]interface{}{"key": "v1"} - - options := &snapshots.UpdateMetadataOpts{ - Metadata: map[string]interface{}{ - "key": "v1", - }, - } - - actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() - - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, actual, expected) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateResponse(t) - - var name = "snapshot-002" - var description = "Daily backup 002" - options := snapshots.UpdateOpts{Name: &name, Description: &description} - v, err := snapshots.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() - th.AssertNoErr(t, err) - th.CheckEquals(t, "snapshot-002", v.Name) - th.CheckEquals(t, "Daily backup 002", v.Description) -} diff --git a/openstack/evs/v3/snapshots/urls.go b/openstack/evs/v3/snapshots/urls.go index d0bcbfb98..4cd5fee4e 100644 --- a/openstack/evs/v3/snapshots/urls.go +++ b/openstack/evs/v3/snapshots/urls.go @@ -1,31 +1,31 @@ package snapshots -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" -func createURL(c *gophercloud.ServiceClient) string { +func createURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("snapshots") } -func deleteURL(c *gophercloud.ServiceClient, id string) string { +func deleteURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("snapshots", id) } -func getURL(c *gophercloud.ServiceClient, id string) string { +func getURL(c *golangsdk.ServiceClient, id string) string { return deleteURL(c, id) } -func listURL(c *gophercloud.ServiceClient) string { +func listURL(c *golangsdk.ServiceClient) string { return createURL(c) } -func updateURL(c *gophercloud.ServiceClient, id string) string { +func updateURL(c *golangsdk.ServiceClient, id string) string { return deleteURL(c, id) } -func metadataURL(c *gophercloud.ServiceClient, id string) string { +func metadataURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("snapshots", id, "metadata") } -func updateMetadataURL(c *gophercloud.ServiceClient, id string) string { +func updateMetadataURL(c *golangsdk.ServiceClient, id string) string { return metadataURL(c, id) } diff --git a/openstack/evs/v3/snapshots/util.go b/openstack/evs/v3/snapshots/util.go index 40fbb827b..2375926d2 100644 --- a/openstack/evs/v3/snapshots/util.go +++ b/openstack/evs/v3/snapshots/util.go @@ -1,13 +1,13 @@ package snapshots import ( - "github.com/gophercloud/gophercloud" + "github.com/opentelekomcloud/gophertelekomcloud" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { + return golangsdk.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() if err != nil { return false, err diff --git a/openstack/evs/v3/volumes/requests.go b/openstack/evs/v3/volumes/requests.go index f6063c595..2540fee01 100644 --- a/openstack/evs/v3/volumes/requests.go +++ b/openstack/evs/v3/volumes/requests.go @@ -1,8 +1,8 @@ package volumes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the @@ -48,22 +48,22 @@ type CreateOpts struct { // ToVolumeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "volume") + return golangsdk.BuildRequestBody(opts, "volume") } // Create will create a new Volume based on the values in CreateOpts. To extract // the Volume object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -82,12 +82,12 @@ type DeleteOpts struct { // ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string. func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // Delete will delete the existing Volume with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { +func Delete(client *golangsdk.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { url := deleteURL(client, id) if opts != nil { query, err := opts.ToVolumeDeleteQuery() @@ -98,15 +98,15 @@ func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder url += query } resp, err := client.Delete(url, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { +func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -151,12 +151,12 @@ type ListOpts struct { // ToVolumeListQuery formats a ListOpts into a query string. func (opts ListOpts) ToVolumeListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // List returns Volumes optionally limited by the conditions provided in ListOpts. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { +func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToVolumeListQuery() @@ -189,20 +189,20 @@ type UpdateOpts struct { // ToVolumeUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "volume") + return golangsdk.BuildRequestBody(opts, "volume") } // Update will update the Volume with provided information. To extract the updated // Volume from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVolumeUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } diff --git a/openstack/evs/v3/volumes/results.go b/openstack/evs/v3/volumes/results.go index 6f46685b6..667d2ea63 100644 --- a/openstack/evs/v3/volumes/results.go +++ b/openstack/evs/v3/volumes/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Attachment represents a Volume Attachment record @@ -24,7 +24,7 @@ func (r *Attachment) UnmarshalJSON(b []byte) error { type tmp Attachment var s struct { tmp - AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` + AttachedAt golangsdk.JSONRFC3339MilliNoZ `json:"attached_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -89,8 +89,8 @@ func (r *Volume) UnmarshalJSON(b []byte) error { type tmp Volume var s struct { tmp - CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -117,13 +117,13 @@ func (r VolumePage) IsEmpty() (bool, error) { func (page VolumePage) NextPageURL() (string, error) { var s struct { - Links []gophercloud.Link `json:"volumes_links"` + Links []golangsdk.Link `json:"volumes_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } - return gophercloud.ExtractNextURL(s.Links) + return golangsdk.ExtractNextURL(s.Links) } // ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. @@ -134,7 +134,7 @@ func ExtractVolumes(r pagination.Page) ([]Volume, error) { } type commonResult struct { - gophercloud.Result + golangsdk.Result } // Extract will get the Volume object out of the commonResult object. @@ -171,5 +171,5 @@ type UpdateResult struct { // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } diff --git a/openstack/evs/v3/volumes/testing/doc.go b/openstack/evs/v3/volumes/testing/doc.go deleted file mode 100644 index a2b24b7c1..000000000 --- a/openstack/evs/v3/volumes/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// volumes_v3 -package testing diff --git a/openstack/evs/v3/volumes/testing/fixtures.go b/openstack/evs/v3/volumes/testing/fixtures.go deleted file mode 100644 index 339cb30c9..000000000 --- a/openstack/evs/v3/volumes/testing/fixtures.go +++ /dev/null @@ -1,265 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/detail", 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) - - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, ` - { - "volumes": [ - { - "volume_type": "lvmdriver-1", - "created_at": "2015-09-17T03:35:03.000000", - "bootable": "false", - "name": "vol-001", - "os-vol-mig-status-attr:name_id": null, - "consistencygroup_id": null, - "source_volid": null, - "os-volume-replication:driver_data": null, - "multiattach": false, - "snapshot_id": null, - "replication_status": "disabled", - "os-volume-replication:extended_status": null, - "encrypted": false, - "os-vol-host-attr:host": "host-001", - "availability_zone": "nova", - "attachments": [{ - "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", - "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", - "attached_at": "2016-08-06T14:48:20.000000", - "host_name": "foobar", - "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - "device": "/dev/vdc", - "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" - }], - "id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "size": 75, - "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", - "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", - "os-vol-mig-status-attr:migstat": null, - "metadata": {"foo": "bar"}, - "status": "available", - "description": null - }, - { - "volume_type": "lvmdriver-1", - "created_at": "2015-09-17T03:32:29.000000", - "bootable": "false", - "name": "vol-002", - "os-vol-mig-status-attr:name_id": null, - "consistencygroup_id": null, - "source_volid": null, - "os-volume-replication:driver_data": null, - "multiattach": false, - "snapshot_id": null, - "replication_status": "disabled", - "os-volume-replication:extended_status": null, - "encrypted": false, - "os-vol-host-attr:host": null, - "availability_zone": "nova", - "attachments": [], - "id": "96c3bda7-c82a-4f50-be73-ca7621794835", - "size": 75, - "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", - "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", - "os-vol-mig-status-attr:migstat": null, - "metadata": {}, - "status": "available", - "description": null - } - ], - "volumes_links": [ - { - "href": "%s/volumes/detail?marker=1", - "rel": "next" - }] -} - `, th.Server.URL) - case "1": - fmt.Fprintf(w, `{"volumes": []}`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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": { - "volume_type": "lvmdriver-1", - "created_at": "2015-09-17T03:32:29.000000", - "bootable": "false", - "name": "vol-001", - "os-vol-mig-status-attr:name_id": null, - "consistencygroup_id": null, - "source_volid": null, - "os-volume-replication:driver_data": null, - "multiattach": false, - "snapshot_id": null, - "replication_status": "disabled", - "os-volume-replication:extended_status": null, - "encrypted": false, - "availability_zone": "nova", - "attachments": [{ - "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", - "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", - "attached_at": "2016-08-06T14:48:20.000000", - "host_name": "foobar", - "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - "device": "/dev/vdc", - "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" - }], - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "size": 75, - "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", - "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", - "os-vol-mig-status-attr:migstat": null, - "metadata": {}, - "status": "available", - "volume_image_metadata": { - "container_format": "bare", - "image_name": "centos" - }, - "description": null - } -} - `) - }) -} - -func MockCreateResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes", 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, ` -{ - "volume": { - "name": "vol-001", - "size": 75 - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, ` -{ - "volume": { - "size": 75, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "metadata": {}, - "created_at": "2015-09-17T03:32:29.044216", - "encrypted": false, - "bootable": "false", - "availability_zone": "nova", - "attachments": [], - "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", - "status": "creating", - "description": null, - "volume_type": "lvmdriver-1", - "name": "vol-001", - "replication_status": "disabled", - "consistencygroup_id": null, - "source_volid": null, - "snapshot_id": null, - "multiattach": false - } -} - `) - }) -} - -func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockUpdateResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "volume": { - "name": "vol-002" - } -} - `) - }) -} - -func MockCreateVolumeFromBackupResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes", 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, ` -{ - "volume": { - "name": "vol-001", - "backup_id": "20c792f0-bb03-434f-b653-06ef238e337e" - } -} -`) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, ` -{ - "volume": { - "size": 30, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "metadata": {}, - "created_at": "2015-09-17T03:32:29.044216", - "encrypted": false, - "bootable": "false", - "availability_zone": "nova", - "attachments": [], - "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", - "status": "creating", - "description": null, - "volume_type": "lvmdriver-1", - "name": "vol-001", - "replication_status": "disabled", - "consistencygroup_id": null, - "source_volid": null, - "snapshot_id": null, - "backup_id": "20c792f0-bb03-434f-b653-06ef238e337e", - "multiattach": false - } -}`) - }) -} diff --git a/openstack/evs/v3/volumes/testing/requests_test.go b/openstack/evs/v3/volumes/testing/requests_test.go deleted file mode 100644 index a4ecdae23..000000000 --- a/openstack/evs/v3/volumes/testing/requests_test.go +++ /dev/null @@ -1,282 +0,0 @@ -package testing - -import ( - "testing" - "time" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumehost" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetenants" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestListWithExtensions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - count := 0 - - volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := volumes.ExtractVolumes(page) - if err != nil { - t.Errorf("Failed to extract volumes: %v", err) - return false, err - } - - expected := []volumes.Volume{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "vol-001", - Attachments: []volumes.Attachment{{ - ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", - AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", - AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), - HostName: "foobar", - VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - Device: "/dev/vdc", - ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - }}, - AvailabilityZone: "nova", - Bootable: "false", - ConsistencyGroupID: "", - CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), - Description: "", - Encrypted: false, - Metadata: map[string]string{"foo": "bar"}, - Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", - //ReplicationDriverData: "", - //ReplicationExtendedStatus: "", - ReplicationStatus: "disabled", - Size: 75, - SnapshotID: "", - SourceVolID: "", - Status: "available", - UserID: "ff1ce52c03ab433aaba9108c2e3ef541", - VolumeType: "lvmdriver-1", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "vol-002", - Attachments: []volumes.Attachment{}, - AvailabilityZone: "nova", - Bootable: "false", - ConsistencyGroupID: "", - CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), - Description: "", - Encrypted: false, - Metadata: map[string]string{}, - Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", - //ReplicationDriverData: "", - //ReplicationExtendedStatus: "", - ReplicationStatus: "disabled", - Size: 75, - SnapshotID: "", - SourceVolID: "", - Status: "available", - UserID: "ff1ce52c03ab433aaba9108c2e3ef541", - VolumeType: "lvmdriver-1", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestListAllWithExtensions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - type VolumeWithExt struct { - volumes.Volume - volumetenants.VolumeTenantExt - volumehost.VolumeHostExt - } - - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() - th.AssertNoErr(t, err) - - var actual []VolumeWithExt - err = volumes.ExtractVolumesInto(allPages, &actual) - th.AssertNoErr(t, err) - th.AssertEquals(t, 2, len(actual)) - th.AssertEquals(t, "host-001", actual[0].Host) - th.AssertEquals(t, "", actual[1].Host) - th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", actual[0].TenantID) -} - -func TestListAll(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() - th.AssertNoErr(t, err) - actual, err := volumes.ExtractVolumes(allPages) - th.AssertNoErr(t, err) - - expected := []volumes.Volume{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "vol-001", - Attachments: []volumes.Attachment{{ - ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", - AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", - AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), - HostName: "foobar", - VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - Device: "/dev/vdc", - ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - }}, - AvailabilityZone: "nova", - Bootable: "false", - ConsistencyGroupID: "", - CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), - Description: "", - Encrypted: false, - Metadata: map[string]string{"foo": "bar"}, - Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", - //ReplicationDriverData: "", - //ReplicationExtendedStatus: "", - ReplicationStatus: "disabled", - Size: 75, - SnapshotID: "", - SourceVolID: "", - Status: "available", - UserID: "ff1ce52c03ab433aaba9108c2e3ef541", - VolumeType: "lvmdriver-1", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "vol-002", - Attachments: []volumes.Attachment{}, - AvailabilityZone: "nova", - Bootable: "false", - ConsistencyGroupID: "", - CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), - Description: "", - Encrypted: false, - Metadata: map[string]string{}, - Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", - //ReplicationDriverData: "", - //ReplicationExtendedStatus: "", - ReplicationStatus: "disabled", - Size: 75, - SnapshotID: "", - SourceVolID: "", - Status: "available", - UserID: "ff1ce52c03ab433aaba9108c2e3ef541", - VolumeType: "lvmdriver-1", - }, - } - - th.CheckDeepEquals(t, expected, actual) - -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - v, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, v.Name, "vol-001") - th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - options := &volumes.CreateOpts{Size: 75, Name: "vol-001"} - n, err := volumes.Create(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.Size, 75) - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", volumes.DeleteOpts{}) - th.AssertNoErr(t, res.Err) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateResponse(t) - - var name = "vol-002" - options := volumes.UpdateOpts{Name: &name} - v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() - th.AssertNoErr(t, err) - th.CheckEquals(t, "vol-002", v.Name) -} - -func TestGetWithExtensions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - var s struct { - volumes.Volume - volumetenants.VolumeTenantExt - } - err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) - th.AssertNoErr(t, err) - th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", s.TenantID) - th.AssertEquals(t, "centos", s.Volume.VolumeImageMetadata["image_name"]) - - err = volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) - if err == nil { - t.Errorf("Expected error when providing non-pointer struct") - } -} - -func TestCreateFromBackup(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateVolumeFromBackupResponse(t) - - options := volumes.CreateOpts{ - Name: "vol-001", - BackupID: "20c792f0-bb03-434f-b653-06ef238e337e", - } - - v, err := volumes.Create(client.ServiceClient(), options).Extract() - - th.AssertNoErr(t, err) - th.AssertEquals(t, v.Size, 30) - th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertEquals(t, *v.BackupID, "20c792f0-bb03-434f-b653-06ef238e337e") -} diff --git a/openstack/evs/v3/volumes/urls.go b/openstack/evs/v3/volumes/urls.go index 170724905..f145a5be2 100644 --- a/openstack/evs/v3/volumes/urls.go +++ b/openstack/evs/v3/volumes/urls.go @@ -1,23 +1,23 @@ package volumes -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" -func createURL(c *gophercloud.ServiceClient) string { +func createURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("volumes") } -func listURL(c *gophercloud.ServiceClient) string { +func listURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("volumes", "detail") } -func deleteURL(c *gophercloud.ServiceClient, id string) string { +func deleteURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("volumes", id) } -func getURL(c *gophercloud.ServiceClient, id string) string { +func getURL(c *golangsdk.ServiceClient, id string) string { return deleteURL(c, id) } -func updateURL(c *gophercloud.ServiceClient, id string) string { +func updateURL(c *golangsdk.ServiceClient, id string) string { return deleteURL(c, id) } diff --git a/openstack/evs/v3/volumes/util.go b/openstack/evs/v3/volumes/util.go index e86c1b4b4..49c848a62 100644 --- a/openstack/evs/v3/volumes/util.go +++ b/openstack/evs/v3/volumes/util.go @@ -1,13 +1,13 @@ package volumes import ( - "github.com/gophercloud/gophercloud" + "github.com/opentelekomcloud/gophertelekomcloud" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { + return golangsdk.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() if err != nil { return false, err diff --git a/openstack/evs/v3/volumetypes/requests.go b/openstack/evs/v3/volumetypes/requests.go index 5b272bf05..5f9c88ca5 100644 --- a/openstack/evs/v3/volumetypes/requests.go +++ b/openstack/evs/v3/volumetypes/requests.go @@ -1,8 +1,8 @@ package volumetypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the @@ -28,37 +28,37 @@ type CreateOpts struct { // ToVolumeTypeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "volume_type") + return golangsdk.BuildRequestBody(opts, "volume_type") } // Create will create a new Volume Type based on the values in CreateOpts. To extract // the Volume Type object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeTypeCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Delete will delete the existing Volume Type with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { +func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // Get retrieves the Volume Type with the provided ID. To extract the Volume Type object // from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { +func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -84,12 +84,12 @@ type ListOpts struct { // ToVolumeTypeListQuery formats a ListOpts into a query string. func (opts ListOpts) ToVolumeTypeListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) + q, err := golangsdk.BuildQueryString(opts) return q.String(), err } // List returns Volume types. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { +func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { @@ -123,35 +123,35 @@ type UpdateOpts struct { // ToVolumeTypeUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "volume_type") + return golangsdk.BuildRequestBody(opts, "volume_type") } // Update will update the Volume Type with provided information. To extract the updated // Volume Type from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVolumeTypeUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // ListExtraSpecs requests all the extra-specs for the given volume type ID. -func ListExtraSpecs(client *gophercloud.ServiceClient, volumeTypeID string) (r ListExtraSpecsResult) { +func ListExtraSpecs(client *golangsdk.ServiceClient, volumeTypeID string) (r ListExtraSpecsResult) { resp, err := client.Get(extraSpecsListURL(client, volumeTypeID), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // GetExtraSpec requests an extra-spec specified by key for the given volume type ID -func GetExtraSpec(client *gophercloud.ServiceClient, volumeTypeID string, key string) (r GetExtraSpecResult) { +func GetExtraSpec(client *golangsdk.ServiceClient, volumeTypeID string, key string) (r GetExtraSpecResult) { resp, err := client.Get(extraSpecsGetURL(client, volumeTypeID, key), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -172,16 +172,16 @@ func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecsCreateMap() (map[string]interfa // CreateExtraSpecs will create or update the extra-specs key-value pairs for // the specified volume type. -func CreateExtraSpecs(client *gophercloud.ServiceClient, volumeTypeID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { +func CreateExtraSpecs(client *golangsdk.ServiceClient, volumeTypeID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { b, err := opts.ToVolumeTypeExtraSpecsCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(extraSpecsCreateURL(client, volumeTypeID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(extraSpecsCreateURL(client, volumeTypeID), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -195,7 +195,7 @@ type UpdateExtraSpecOptsBuilder interface { // the contents of a ExtraSpecOpts. func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecUpdateMap() (map[string]string, string, error) { if len(opts) != 1 { - err := gophercloud.ErrInvalidInput{} + err := golangsdk.ErrInvalidInput{} err.Argument = "volumetypes.ExtraSpecOpts" err.Info = "Must have one and only one key-value pair" return nil, "", err @@ -211,31 +211,31 @@ func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecUpdateMap() (map[string]string, // UpdateExtraSpec will updates the value of the specified volume type's extra spec // for the key in opts. -func UpdateExtraSpec(client *gophercloud.ServiceClient, volumeTypeID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { +func UpdateExtraSpec(client *golangsdk.ServiceClient, volumeTypeID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { b, key, err := opts.ToVolumeTypeExtraSpecUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(extraSpecUpdateURL(client, volumeTypeID, key), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(extraSpecUpdateURL(client, volumeTypeID, key), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // DeleteExtraSpec will delete the key-value pair with the given key for the given // volume type ID. -func DeleteExtraSpec(client *gophercloud.ServiceClient, volumeTypeID, key string) (r DeleteExtraSpecResult) { - resp, err := client.Delete(extraSpecDeleteURL(client, volumeTypeID, key), &gophercloud.RequestOpts{ +func DeleteExtraSpec(client *golangsdk.ServiceClient, volumeTypeID, key string) (r DeleteExtraSpecResult) { + resp, err := client.Delete(extraSpecDeleteURL(client, volumeTypeID, key), &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } // ListAccesses retrieves the tenants which have access to a volume type. -func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager { +func ListAccesses(client *golangsdk.ServiceClient, id string) pagination.Pager { url := accessURL(client, id) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { @@ -257,20 +257,20 @@ type AddAccessOpts struct { // ToVolumeTypeAddAccessMap constructs a request body from AddAccessOpts. func (opts AddAccessOpts) ToVolumeTypeAddAccessMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "addProjectAccess") + return golangsdk.BuildRequestBody(opts, "addProjectAccess") } // AddAccess grants a tenant/project access to a volume type. -func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { +func AddAccess(client *golangsdk.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { b, err := opts.ToVolumeTypeAddAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(accessActionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(accessActionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -288,19 +288,19 @@ type RemoveAccessOpts struct { // ToVolumeTypeRemoveAccessMap constructs a request body from RemoveAccessOpts. func (opts RemoveAccessOpts) ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "removeProjectAccess") + return golangsdk.BuildRequestBody(opts, "removeProjectAccess") } // RemoveAccess removes/revokes a tenant/project access to a volume type. -func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { +func RemoveAccess(client *golangsdk.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { b, err := opts.ToVolumeTypeRemoveAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(accessActionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(accessActionURL(client, id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } diff --git a/openstack/evs/v3/volumetypes/results.go b/openstack/evs/v3/volumetypes/results.go index 72c696ef1..e58873407 100644 --- a/openstack/evs/v3/volumetypes/results.go +++ b/openstack/evs/v3/volumetypes/results.go @@ -1,8 +1,8 @@ package volumetypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // VolumeType contains all the information associated with an OpenStack Volume Type. @@ -36,13 +36,13 @@ func (r VolumeTypePage) IsEmpty() (bool, error) { func (page VolumeTypePage) NextPageURL() (string, error) { var s struct { - Links []gophercloud.Link `json:"volume_type_links"` + Links []golangsdk.Link `json:"volume_type_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } - return gophercloud.ExtractNextURL(s.Links) + return golangsdk.ExtractNextURL(s.Links) } // ExtractVolumeTypes extracts and returns Volumes. It is used while iterating over a volumetypes.List call. @@ -53,7 +53,7 @@ func ExtractVolumeTypes(r pagination.Page) ([]VolumeType, error) { } type commonResult struct { - gophercloud.Result + golangsdk.Result } // Extract will get the Volume Type object out of the commonResult object. @@ -85,7 +85,7 @@ type CreateResult struct { // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // UpdateResult contains the response body and error from an Update request. @@ -97,7 +97,7 @@ type UpdateResult struct { // key-value pairs. Call its Extract method to interpret it as a // map[string]interface. type extraSpecsResult struct { - gophercloud.Result + golangsdk.Result } // ListExtraSpecsResult contains the result of a Get operation. Call its Extract @@ -124,7 +124,7 @@ func (r extraSpecsResult) Extract() (map[string]string, error) { // extraSpecResult contains the result of a call for individual a single // key-value pair. type extraSpecResult struct { - gophercloud.Result + golangsdk.Result } // GetExtraSpecResult contains the result of a Get operation. Call its Extract @@ -142,7 +142,7 @@ type UpdateExtraSpecResult struct { // DeleteExtraSpecResult contains the result of a Delete operation. Call its // ExtractErr method to determine if the call succeeded or failed. type DeleteExtraSpecResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // Extract interprets any extraSpecResult as an ExtraSpec, if possible. @@ -184,11 +184,11 @@ func ExtractAccesses(r pagination.Page) ([]VolumeTypeAccess, error) { // AddAccessResult is the response from a AddAccess request. Call its // ExtractErr method to determine if the request succeeded or failed. type AddAccessResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } // RemoveAccessResult is the response from a RemoveAccess request. Call its // ExtractErr method to determine if the request succeeded or failed. type RemoveAccessResult struct { - gophercloud.ErrResult + golangsdk.ErrResult } diff --git a/openstack/evs/v3/volumetypes/testing/doc.go b/openstack/evs/v3/volumetypes/testing/doc.go deleted file mode 100644 index 3fd720a67..000000000 --- a/openstack/evs/v3/volumetypes/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// volume_types -package testing diff --git a/openstack/evs/v3/volumetypes/testing/fixtures.go b/openstack/evs/v3/volumetypes/testing/fixtures.go deleted file mode 100644 index eb617f19e..000000000 --- a/openstack/evs/v3/volumetypes/testing/fixtures.go +++ /dev/null @@ -1,260 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -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) - - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, ` -{ - "volume_types": [ - { - "name": "SSD", - "qos_specs_id": null, - "os-volume-type-access:is_public": true, - "extra_specs": { - "volume_backend_name": "lvmdriver-1" - }, - "is_public": true, - "id": "6685584b-1eac-4da6-b5c3-555430cf68ff", - "description": null - }, - { - "name": "SATA", - "qos_specs_id": null, - "os-volume-type-access:is_public": true, - "extra_specs": { - "volume_backend_name": "lvmdriver-1" - }, - "is_public": true, - "id": "8eb69a46-df97-4e41-9586-9a40a7533803", - "description": null - } - ], - "volume_type_links": [ - { - "href": "%s/types?marker=1", - "rel": "next" - } - ] -} - `, th.Server.URL) - case "1": - fmt.Fprintf(w, `{"volume_types": []}`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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_type": { - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "name": "vol-type-001", - "os-volume-type-access:is_public": true, - "qos_specs_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "description": "volume type 001", - "is_public": true, - "extra_specs": { - "capabilities": "gpu" - } - } -} -`) - }) -} - -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, ` -{ - "volume_type": { - "name": "test_type", - "os-volume-type-access:is_public": true, - "description": "test_type_desc", - "extra_specs": { - "capabilities": "gpu" - } - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "volume_type": { - "name": "test_type", - "extra_specs": {}, - "is_public": true, - "os-volume-type-access:is_public": true, - "id": "6d0ff92a-0007-4780-9ece-acfe5876966a", - "description": "test_type_desc", - "extra_specs": { - "capabilities": "gpu" - } - } -} - `) - }) -} - -func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockUpdateResponse(t *testing.T) { - th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "volume_type": { - "name": "vol-type-002", - "description": "volume type 0001", - "is_public": true, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" - } -}`) - }) -} - -// ExtraSpecsGetBody provides a GET result of the extra_specs for a volume type -const ExtraSpecsGetBody = ` -{ - "extra_specs" : { - "capabilities": "gpu", - "volume_backend_name": "ssd" - } -} -` - -// GetExtraSpecBody provides a GET result of a particular extra_spec for a volume type -const GetExtraSpecBody = ` -{ - "capabilities": "gpu" -} -` - -// UpdatedExtraSpecBody provides an PUT result of a particular updated extra_spec for a volume type -const UpdatedExtraSpecBody = ` -{ - "capabilities": "gpu-2" -} -` - -// ExtraSpecs is the expected extra_specs returned from GET on a volume type's extra_specs -var ExtraSpecs = map[string]string{ - "capabilities": "gpu", - "volume_backend_name": "ssd", -} - -// ExtraSpec is the expected extra_spec returned from GET on a volume type's extra_specs -var ExtraSpec = map[string]string{ - "capabilities": "gpu", -} - -// UpdatedExtraSpec is the expected extra_spec returned from PUT on a volume type's extra_specs -var UpdatedExtraSpec = map[string]string{ - "capabilities": "gpu-2", -} - -func HandleExtraSpecsListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/types/1/extra_specs", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestHeader(t, r, "Accept", "application/json") - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ExtraSpecsGetBody) - }) -} - -func HandleExtraSpecGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/types/1/extra_specs/capabilities", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestHeader(t, r, "Accept", "application/json") - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetExtraSpecBody) - }) -} - -func HandleExtraSpecsCreateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/types/1/extra_specs", 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, "Accept", "application/json") - th.TestJSONRequest(t, r, `{ - "extra_specs": { - "capabilities": "gpu", - "volume_backend_name": "ssd" - } - }`) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ExtraSpecsGetBody) - }) -} - -func HandleExtraSpecUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/types/1/extra_specs/capabilities", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestHeader(t, r, "Accept", "application/json") - th.TestJSONRequest(t, r, `{ - "capabilities": "gpu-2" - }`) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdatedExtraSpecBody) - }) -} - -func HandleExtraSpecDeleteSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/types/1/extra_specs/capabilities", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/openstack/evs/v3/volumetypes/testing/requests_test.go b/openstack/evs/v3/volumetypes/testing/requests_test.go deleted file mode 100644 index eb6f2e7c0..000000000 --- a/openstack/evs/v3/volumetypes/testing/requests_test.go +++ /dev/null @@ -1,278 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "reflect" - "testing" - - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestListAll(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - pages := 0 - err := volumetypes.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { - pages++ - actual, err := volumetypes.ExtractVolumeTypes(page) - if err != nil { - return false, err - } - expected := []volumetypes.VolumeType{ - { - ID: "6685584b-1eac-4da6-b5c3-555430cf68ff", - Name: "SSD", - ExtraSpecs: map[string]string{"volume_backend_name": "lvmdriver-1"}, - IsPublic: true, - Description: "", - QosSpecID: "", - PublicAccess: true, - }, { - ID: "8eb69a46-df97-4e41-9586-9a40a7533803", - Name: "SATA", - ExtraSpecs: map[string]string{"volume_backend_name": "lvmdriver-1"}, - IsPublic: true, - Description: "", - QosSpecID: "", - PublicAccess: true, - }, - } - th.CheckDeepEquals(t, expected, actual) - return true, nil - }) - th.AssertNoErr(t, err) - th.AssertEquals(t, pages, 1) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - v, err := volumetypes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, v.Name, "vol-type-001") - th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertEquals(t, v.ExtraSpecs["capabilities"], "gpu") - th.AssertEquals(t, v.QosSpecID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertEquals(t, v.PublicAccess, true) -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - var isPublic = true - - options := &volumetypes.CreateOpts{ - Name: "test_type", - IsPublic: &isPublic, - Description: "test_type_desc", - ExtraSpecs: map[string]string{"capabilities": "gpu"}, - } - - n, err := volumetypes.Create(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.Name, "test_type") - th.AssertEquals(t, n.Description, "test_type_desc") - th.AssertEquals(t, n.IsPublic, true) - th.AssertEquals(t, n.PublicAccess, true) - th.AssertEquals(t, n.ID, "6d0ff92a-0007-4780-9ece-acfe5876966a") - th.AssertEquals(t, n.ExtraSpecs["capabilities"], "gpu") -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := volumetypes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateResponse(t) - - var isPublic = true - var name = "vol-type-002" - options := volumetypes.UpdateOpts{ - Name: &name, - IsPublic: &isPublic, - } - - v, err := volumetypes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() - th.AssertNoErr(t, err) - th.CheckEquals(t, "vol-type-002", v.Name) - th.CheckEquals(t, true, v.IsPublic) -} - -func TestVolumeTypeExtraSpecsList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleExtraSpecsListSuccessfully(t) - - expected := ExtraSpecs - actual, err := volumetypes.ListExtraSpecs(client.ServiceClient(), "1").Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, expected, actual) -} - -func TestVolumeTypeExtraSpecGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleExtraSpecGetSuccessfully(t) - - expected := ExtraSpec - actual, err := volumetypes.GetExtraSpec(client.ServiceClient(), "1", "capabilities").Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, expected, actual) -} - -func TestVolumeTypeExtraSpecsCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleExtraSpecsCreateSuccessfully(t) - - createOpts := volumetypes.ExtraSpecsOpts{ - "capabilities": "gpu", - "volume_backend_name": "ssd", - } - expected := ExtraSpecs - actual, err := volumetypes.CreateExtraSpecs(client.ServiceClient(), "1", createOpts).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, expected, actual) -} - -func TestVolumeTypeExtraSpecUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleExtraSpecUpdateSuccessfully(t) - - updateOpts := volumetypes.ExtraSpecsOpts{ - "capabilities": "gpu-2", - } - expected := UpdatedExtraSpec - actual, err := volumetypes.UpdateExtraSpec(client.ServiceClient(), "1", updateOpts).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, expected, actual) -} - -func TestVolumeTypeExtraSpecDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleExtraSpecDeleteSuccessfully(t) - - res := volumetypes.DeleteExtraSpec(client.ServiceClient(), "1", "capabilities") - th.AssertNoErr(t, res.Err) -} - -func TestVolumeTypeListAccesses(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/os-volume-type-access", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` - { - "volume_type_access": [ - { - "project_id": "6f70656e737461636b20342065766572", - "volume_type_id": "a5082c24-2a27-43a4-b48e-fcec1240e36b" - } - ] - } - `) - }) - - expected := []volumetypes.VolumeTypeAccess{ - { - VolumeTypeID: "a5082c24-2a27-43a4-b48e-fcec1240e36b", - ProjectID: "6f70656e737461636b20342065766572", - }, - } - - allPages, err := volumetypes.ListAccesses(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b").AllPages() - th.AssertNoErr(t, err) - - actual, err := volumetypes.ExtractAccesses(allPages) - th.AssertNoErr(t, err) - - if !reflect.DeepEqual(expected, actual) { - t.Errorf("Expected %#v, but was %#v", expected, actual) - } -} - -func TestVolumeTypeAddAccess(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestHeader(t, r, "accept", "application/json") - th.TestJSONRequest(t, r, ` - { - "addProjectAccess": { - "project": "6f70656e737461636b20342065766572" - } - } - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - }) - - addAccessOpts := volumetypes.AddAccessOpts{ - Project: "6f70656e737461636b20342065766572", - } - - err := volumetypes.AddAccess(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", addAccessOpts).ExtractErr() - th.AssertNoErr(t, err) - -} - -func TestVolumeTypeRemoveAccess(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestHeader(t, r, "accept", "application/json") - th.TestJSONRequest(t, r, ` - { - "removeProjectAccess": { - "project": "6f70656e737461636b20342065766572" - } - } - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - }) - - removeAccessOpts := volumetypes.RemoveAccessOpts{ - Project: "6f70656e737461636b20342065766572", - } - - err := volumetypes.RemoveAccess(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", removeAccessOpts).ExtractErr() - th.AssertNoErr(t, err) - -} diff --git a/openstack/evs/v3/volumetypes/urls.go b/openstack/evs/v3/volumetypes/urls.go index c63ee47e6..ee92ffd6b 100644 --- a/openstack/evs/v3/volumetypes/urls.go +++ b/openstack/evs/v3/volumetypes/urls.go @@ -1,51 +1,51 @@ package volumetypes -import "github.com/gophercloud/gophercloud" +import "github.com/opentelekomcloud/gophertelekomcloud" -func listURL(c *gophercloud.ServiceClient) string { +func listURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("types") } -func getURL(c *gophercloud.ServiceClient, id string) string { +func getURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("types", id) } -func createURL(c *gophercloud.ServiceClient) string { +func createURL(c *golangsdk.ServiceClient) string { return c.ServiceURL("types") } -func deleteURL(c *gophercloud.ServiceClient, id string) string { +func deleteURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("types", id) } -func updateURL(c *gophercloud.ServiceClient, id string) string { +func updateURL(c *golangsdk.ServiceClient, id string) string { return c.ServiceURL("types", id) } -func extraSpecsListURL(client *gophercloud.ServiceClient, id string) string { +func extraSpecsListURL(client *golangsdk.ServiceClient, id string) string { return client.ServiceURL("types", id, "extra_specs") } -func extraSpecsGetURL(client *gophercloud.ServiceClient, id, key string) string { +func extraSpecsGetURL(client *golangsdk.ServiceClient, id, key string) string { return client.ServiceURL("types", id, "extra_specs", key) } -func extraSpecsCreateURL(client *gophercloud.ServiceClient, id string) string { +func extraSpecsCreateURL(client *golangsdk.ServiceClient, id string) string { return client.ServiceURL("types", id, "extra_specs") } -func extraSpecUpdateURL(client *gophercloud.ServiceClient, id, key string) string { +func extraSpecUpdateURL(client *golangsdk.ServiceClient, id, key string) string { return client.ServiceURL("types", id, "extra_specs", key) } -func extraSpecDeleteURL(client *gophercloud.ServiceClient, id, key string) string { +func extraSpecDeleteURL(client *golangsdk.ServiceClient, id, key string) string { return client.ServiceURL("types", id, "extra_specs", key) } -func accessURL(client *gophercloud.ServiceClient, id string) string { +func accessURL(client *golangsdk.ServiceClient, id string) string { return client.ServiceURL("types", id, "os-volume-type-access") } -func accessActionURL(client *gophercloud.ServiceClient, id string) string { +func accessActionURL(client *golangsdk.ServiceClient, id string) string { return client.ServiceURL("types", id, "action") } From c1e8296eef76b162d0c48d58c31a818d55ffefd1 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 13:30:47 +0100 Subject: [PATCH 03/51] Replace --- .../openstack/evs/extensions/backups_test.go | 39 ++ .../openstack/evs/extensions/extensions.go | 350 ++++++++++++++++++ .../openstack/evs/extensions/limits_test.go | 36 ++ .../evs/extensions/schedulerhints_test.go | 57 +++ .../evs/extensions/schedulerstats_test.go | 31 ++ .../openstack/evs/extensions/services_test.go | 27 ++ .../evs/extensions/volumeactions_test.go | 208 +++++++++++ .../evs/extensions/volumetenants_test.go | 44 +++ .../openstack/evs/noauth/blockstorage.go | 142 +++++++ .../openstack/evs/noauth/snapshots_test.go | 56 +++ .../openstack/evs/noauth/volumes_test.go | 50 +++ acceptance/openstack/evs/v3/blockstorage.go | 321 ++++++++++++++++ acceptance/openstack/evs/v3/qos_test.go | 127 +++++++ acceptance/openstack/evs/v3/quotaset_test.go | 187 ++++++++++ acceptance/openstack/evs/v3/snapshots_test.go | 72 ++++ .../openstack/evs/v3/volumeattachments.go | 110 ++++++ .../evs/v3/volumeattachments_test.go | 37 ++ acceptance/openstack/evs/v3/volumes_test.go | 142 +++++++ .../openstack/evs/v3/volumetypes_test.go | 162 ++++++++ .../blockstorage/v2/snapshots/requests.go | 167 --------- .../blockstorage/v2/snapshots/results.go | 117 ------ .../blockstorage/v2/snapshots/testing/doc.go | 2 - .../v2/snapshots/testing/fixtures.go | 134 ------- .../v2/snapshots/testing/requests_test.go | 116 ------ openstack/blockstorage/v2/snapshots/urls.go | 27 -- openstack/blockstorage/v2/snapshots/util.go | 22 -- openstack/blockstorage/v3/snapshots/doc.go | 5 - .../blockstorage/v3/snapshots/requests.go | 189 ---------- .../blockstorage/v3/snapshots/results.go | 129 ------- .../blockstorage/v3/snapshots/testing/doc.go | 2 - .../v3/snapshots/testing/fixtures.go | 148 -------- .../v3/snapshots/testing/requests_test.go | 116 ------ openstack/blockstorage/v3/snapshots/urls.go | 27 -- openstack/blockstorage/v3/snapshots/util.go | 22 -- openstack/blockstorage/v3/volumes/doc.go | 5 - openstack/blockstorage/v3/volumes/requests.go | 210 ----------- openstack/blockstorage/v3/volumes/results.go | 170 --------- .../blockstorage/v3/volumes/testing/doc.go | 2 - .../v3/volumes/testing/fixtures.go | 217 ----------- .../v3/volumes/testing/requests_test.go | 257 ------------- openstack/blockstorage/v3/volumes/urls.go | 23 -- openstack/blockstorage/v3/volumes/util.go | 22 -- openstack/blockstorage/v3/volumetypes/doc.go | 63 ---- .../blockstorage/v3/volumetypes/requests.go | 141 ------- .../blockstorage/v3/volumetypes/results.go | 94 ----- .../v3/volumetypes/testing/doc.go | 2 - .../v3/volumetypes/testing/fixtures.go | 154 -------- .../v3/volumetypes/testing/requests_test.go | 118 ------ openstack/blockstorage/v3/volumetypes/urls.go | 23 -- .../{blockstorage => evs}/v2/snapshots/doc.go | 0 openstack/evs/v2/snapshots/requests.go | 136 ++++--- openstack/evs/v2/snapshots/results.go | 62 ++-- .../evs/v2/snapshots/testing/fixtures.go | 125 +++---- .../evs/v2/snapshots/testing/requests_test.go | 110 +++--- openstack/evs/v2/snapshots/urls.go | 22 +- .../{blockstorage => evs}/v2/volumes/doc.go | 0 .../v2/volumes/requests.go | 0 .../v2/volumes/results.go | 0 .../v2/volumes/testing/doc.go | 0 .../v2/volumes/testing/fixtures.go | 0 .../v2/volumes/testing/requests_test.go | 0 .../{blockstorage => evs}/v2/volumes/urls.go | 0 .../{blockstorage => evs}/v2/volumes/util.go | 0 63 files changed, 2443 insertions(+), 2934 deletions(-) create mode 100644 acceptance/openstack/evs/extensions/backups_test.go create mode 100644 acceptance/openstack/evs/extensions/extensions.go create mode 100644 acceptance/openstack/evs/extensions/limits_test.go create mode 100644 acceptance/openstack/evs/extensions/schedulerhints_test.go create mode 100644 acceptance/openstack/evs/extensions/schedulerstats_test.go create mode 100644 acceptance/openstack/evs/extensions/services_test.go create mode 100644 acceptance/openstack/evs/extensions/volumeactions_test.go create mode 100644 acceptance/openstack/evs/extensions/volumetenants_test.go create mode 100644 acceptance/openstack/evs/noauth/blockstorage.go create mode 100644 acceptance/openstack/evs/noauth/snapshots_test.go create mode 100644 acceptance/openstack/evs/noauth/volumes_test.go create mode 100644 acceptance/openstack/evs/v3/blockstorage.go create mode 100644 acceptance/openstack/evs/v3/qos_test.go create mode 100644 acceptance/openstack/evs/v3/quotaset_test.go create mode 100644 acceptance/openstack/evs/v3/snapshots_test.go create mode 100644 acceptance/openstack/evs/v3/volumeattachments.go create mode 100644 acceptance/openstack/evs/v3/volumeattachments_test.go create mode 100644 acceptance/openstack/evs/v3/volumes_test.go create mode 100644 acceptance/openstack/evs/v3/volumetypes_test.go delete mode 100644 openstack/blockstorage/v2/snapshots/requests.go delete mode 100644 openstack/blockstorage/v2/snapshots/results.go delete mode 100644 openstack/blockstorage/v2/snapshots/testing/doc.go delete mode 100644 openstack/blockstorage/v2/snapshots/testing/fixtures.go delete mode 100644 openstack/blockstorage/v2/snapshots/testing/requests_test.go delete mode 100644 openstack/blockstorage/v2/snapshots/urls.go delete mode 100644 openstack/blockstorage/v2/snapshots/util.go delete mode 100644 openstack/blockstorage/v3/snapshots/doc.go delete mode 100644 openstack/blockstorage/v3/snapshots/requests.go delete mode 100644 openstack/blockstorage/v3/snapshots/results.go delete mode 100644 openstack/blockstorage/v3/snapshots/testing/doc.go delete mode 100644 openstack/blockstorage/v3/snapshots/testing/fixtures.go delete mode 100644 openstack/blockstorage/v3/snapshots/testing/requests_test.go delete mode 100644 openstack/blockstorage/v3/snapshots/urls.go delete mode 100644 openstack/blockstorage/v3/snapshots/util.go delete mode 100644 openstack/blockstorage/v3/volumes/doc.go delete mode 100644 openstack/blockstorage/v3/volumes/requests.go delete mode 100644 openstack/blockstorage/v3/volumes/results.go delete mode 100644 openstack/blockstorage/v3/volumes/testing/doc.go delete mode 100644 openstack/blockstorage/v3/volumes/testing/fixtures.go delete mode 100644 openstack/blockstorage/v3/volumes/testing/requests_test.go delete mode 100644 openstack/blockstorage/v3/volumes/urls.go delete mode 100644 openstack/blockstorage/v3/volumes/util.go delete mode 100644 openstack/blockstorage/v3/volumetypes/doc.go delete mode 100644 openstack/blockstorage/v3/volumetypes/requests.go delete mode 100644 openstack/blockstorage/v3/volumetypes/results.go delete mode 100644 openstack/blockstorage/v3/volumetypes/testing/doc.go delete mode 100644 openstack/blockstorage/v3/volumetypes/testing/fixtures.go delete mode 100644 openstack/blockstorage/v3/volumetypes/testing/requests_test.go delete mode 100644 openstack/blockstorage/v3/volumetypes/urls.go rename openstack/{blockstorage => evs}/v2/snapshots/doc.go (100%) rename openstack/{blockstorage => evs}/v2/volumes/doc.go (100%) rename openstack/{blockstorage => evs}/v2/volumes/requests.go (100%) rename openstack/{blockstorage => evs}/v2/volumes/results.go (100%) rename openstack/{blockstorage => evs}/v2/volumes/testing/doc.go (100%) rename openstack/{blockstorage => evs}/v2/volumes/testing/fixtures.go (100%) rename openstack/{blockstorage => evs}/v2/volumes/testing/requests_test.go (100%) rename openstack/{blockstorage => evs}/v2/volumes/urls.go (100%) rename openstack/{blockstorage => evs}/v2/volumes/util.go (100%) diff --git a/acceptance/openstack/evs/extensions/backups_test.go b/acceptance/openstack/evs/extensions/backups_test.go new file mode 100644 index 000000000..d4a5d94ec --- /dev/null +++ b/acceptance/openstack/evs/extensions/backups_test.go @@ -0,0 +1,39 @@ +package extensions + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/backups" + + blockstorage "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/evs/v3" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestBackupsCRUD(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + backup, err := CreateBackup(t, blockClient, volume.ID) + th.AssertNoErr(t, err) + defer DeleteBackup(t, blockClient, backup.ID) + + allPages, err := backups.List(blockClient, nil).AllPages() + th.AssertNoErr(t, err) + + allBackups, err := backups.ExtractBackups(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allBackups { + if backup.Name == v.Name { + found = true + } + } + + th.AssertEquals(t, found, true) +} diff --git a/acceptance/openstack/evs/extensions/extensions.go b/acceptance/openstack/evs/extensions/extensions.go new file mode 100644 index 000000000..15fed9d46 --- /dev/null +++ b/acceptance/openstack/evs/extensions/extensions.go @@ -0,0 +1,350 @@ +// Package extensions contains common functions for creating block storage +// resources that are extensions of the block storage API. See the `*_test.go` +// files for example usages. +package extensions + +import ( + "fmt" + "strings" + "testing" + + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/images" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/servers" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/backups" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/volumeactions" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v2/volumes" + v3 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumetypes" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +// CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be +// returned +func CreateUploadImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) (volumeactions.VolumeImage, error) { + if testing.Short() { + t.Skip("Skipping test that requires volume-backed image uploading in short mode.") + } + + imageName := tools.RandomString("ACPTTEST", 16) + uploadImageOpts := volumeactions.UploadImageOpts{ + ImageName: imageName, + Force: true, + } + + volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract() + if err != nil { + return volumeImage, err + } + + t.Logf("Uploading volume %s as volume-backed image %s", volume.ID, imageName) + + if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { + return volumeImage, err + } + + t.Logf("Uploaded volume %s as volume-backed image %s", volume.ID, imageName) + + return volumeImage, nil + +} + +// DeleteUploadedImage deletes uploaded image. An error will be returned +// if the deletion request failed. +func DeleteUploadedImage(t *testing.T, client *golangsdk.ServiceClient, imageID string) error { + if testing.Short() { + t.Skip("Skipping test that requires volume-backed image removing in short mode.") + } + + t.Logf("Removing image %s", imageID) + + err := images.Delete(client, imageID).ExtractErr() + if err != nil { + return err + } + + return nil +} + +// CreateVolumeAttach will attach a volume to an instance. An error will be +// returned if the attachment failed. +func CreateVolumeAttach(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume, server *servers.Server) error { + if testing.Short() { + t.Skip("Skipping test that requires volume attachment in short mode.") + } + + attachOpts := volumeactions.AttachOpts{ + MountPoint: "/mnt", + Mode: "rw", + InstanceUUID: server.ID, + } + + t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) + + if err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr(); err != nil { + return err + } + + if err := volumes.WaitForStatus(client, volume.ID, "in-use", 60); err != nil { + return err + } + + t.Logf("Attached volume %s to server %s", volume.ID, server.ID) + + return nil +} + +// CreateVolumeReserve creates a volume reservation. An error will be returned +// if the reservation failed. +func CreateVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { + if testing.Short() { + t.Skip("Skipping test that requires volume reservation in short mode.") + } + + t.Logf("Attempting to reserve volume %s", volume.ID) + + if err := volumeactions.Reserve(client, volume.ID).ExtractErr(); err != nil { + return err + } + + t.Logf("Reserved volume %s", volume.ID) + + return nil +} + +// DeleteVolumeAttach will detach a volume from an instance. A fatal error will +// occur if the snapshot failed to be deleted. This works best when used as a +// deferred function. +func DeleteVolumeAttach(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) { + t.Logf("Attepting to detach volume volume: %s", volume.ID) + + detachOpts := volumeactions.DetachOpts{ + AttachmentID: volume.Attachments[0].AttachmentID, + } + + if err := volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr(); err != nil { + t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) + } + + if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { + t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) + } + + t.Logf("Detached volume: %s", volume.ID) +} + +// DeleteVolumeReserve deletes a volume reservation. A fatal error will occur +// if the deletion request failed. This works best when used as a deferred +// function. +func DeleteVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) { + if testing.Short() { + t.Skip("Skipping test that requires volume reservation in short mode.") + } + + t.Logf("Attempting to unreserve volume %s", volume.ID) + + if err := volumeactions.Unreserve(client, volume.ID).ExtractErr(); err != nil { + t.Fatalf("Unable to unreserve volume %s: %v", volume.ID, err) + } + + t.Logf("Unreserved volume %s", volume.ID) +} + +// ExtendVolumeSize will extend the size of a volume. +func ExtendVolumeSize(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to extend the size of volume %s", volume.ID) + + extendOpts := volumeactions.ExtendSizeOpts{ + NewSize: 2, + } + + err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr() + if err != nil { + return err + } + + if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { + return err + } + + return nil +} + +// SetImageMetadata will apply the metadata to a volume. +func SetImageMetadata(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to apply image metadata to volume %s", volume.ID) + + imageMetadataOpts := volumeactions.ImageMetadataOpts{ + Metadata: map[string]string{ + "image_name": "testimage", + }, + } + + err := volumeactions.SetImageMetadata(client, volume.ID, imageMetadataOpts).ExtractErr() + if err != nil { + return err + } + + return nil +} + +// CreateBackup will create a backup based on a volume. An error will be +// will be returned if the backup could not be created. +func CreateBackup(t *testing.T, client *golangsdk.ServiceClient, volumeID string) (*backups.Backup, error) { + t.Logf("Attempting to create a backup of volume %s", volumeID) + + backupName := tools.RandomString("ACPTTEST", 16) + createOpts := backups.CreateOpts{ + VolumeID: volumeID, + Name: backupName, + } + + backup, err := backups.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + err = WaitForBackupStatus(client, backup.ID, "available") + if err != nil { + return nil, err + } + + backup, err = backups.Get(client, backup.ID).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created backup %s", backup.ID) + tools.PrintResource(t, backup) + + th.AssertEquals(t, backup.Name, backupName) + + return backup, nil +} + +// DeleteBackup will delete a backup. A fatal error will occur if the backup +// could not be deleted. This works best when used as a deferred function. +func DeleteBackup(t *testing.T, client *golangsdk.ServiceClient, backupID string) { + if err := backups.Delete(client, backupID).ExtractErr(); err != nil { + t.Fatalf("Unable to delete backup %s: %s", backupID, err) + } + + t.Logf("Deleted backup %s", backupID) +} + +// WaitForBackupStatus will continually poll a backup, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForBackupStatus(client *golangsdk.ServiceClient, id, status string) error { + return tools.WaitFor(func() (bool, error) { + current, err := backups.Get(client, id).Extract() + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} + +// SetBootable will set a bootable status to a volume. +func SetBootable(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to apply bootable status to volume %s", volume.ID) + + bootableOpts := volumeactions.BootableOpts{ + Bootable: true, + } + + err := volumeactions.SetBootable(client, volume.ID, bootableOpts).ExtractErr() + if err != nil { + return err + } + + vol, err := v3.Get(client, volume.ID).Extract() + if err != nil { + return err + } + + if strings.ToLower(vol.Bootable) != "true" { + return fmt.Errorf("Volume bootable status is %q, expected 'true'", vol.Bootable) + } + + bootableOpts = volumeactions.BootableOpts{ + Bootable: false, + } + + err = volumeactions.SetBootable(client, volume.ID, bootableOpts).ExtractErr() + if err != nil { + return err + } + + vol, err = v3.Get(client, volume.ID).Extract() + if err != nil { + return err + } + + if strings.ToLower(vol.Bootable) == "true" { + return fmt.Errorf("Volume bootable status is %q, expected 'false'", vol.Bootable) + } + + return nil +} + +// ChangeVolumeType will extend the size of a volume. +func ChangeVolumeType(t *testing.T, client *golangsdk.ServiceClient, volume *v3.Volume, vt *volumetypes.VolumeType) error { + t.Logf("Attempting to change the type of volume %s from %s to %s", volume.ID, volume.VolumeType, vt.Name) + + changeOpts := volumeactions.ChangeTypeOpts{ + NewType: vt.Name, + MigrationPolicy: volumeactions.MigrationPolicyOnDemand, + } + + err := volumeactions.ChangeType(client, volume.ID, changeOpts).ExtractErr() + if err != nil { + return err + } + + if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { + return err + } + + return nil +} + +// ReImage will re-image a volume +func ReImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume, imageID string) error { + t.Logf("Attempting to re-image volume %s", volume.ID) + + reimageOpts := volumeactions.ReImageOpts{ + ImageID: imageID, + ReImageReserved: false, + } + + err := volumeactions.ReImage(client, volume.ID, reimageOpts).ExtractErr() + if err != nil { + return err + } + + err = volumes.WaitForStatus(client, volume.ID, "available", 60) + if err != nil { + return err + } + + vol, err := v3.Get(client, volume.ID).Extract() + if err != nil { + return err + } + + if vol.VolumeImageMetadata == nil { + return fmt.Errorf("volume does not have VolumeImageMetadata map") + } + + if strings.ToLower(vol.VolumeImageMetadata["image_id"]) != imageID { + return fmt.Errorf("volume image id '%s', expected '%s'", vol.VolumeImageMetadata["image_id"], imageID) + } + + return nil +} diff --git a/acceptance/openstack/evs/extensions/limits_test.go b/acceptance/openstack/evs/extensions/limits_test.go new file mode 100644 index 000000000..7b76f9842 --- /dev/null +++ b/acceptance/openstack/evs/extensions/limits_test.go @@ -0,0 +1,36 @@ +package extensions + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/limits" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestLimits(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + limits, err := limits.Get(client).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, limits) + + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalVolumes, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalSnapshots, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalVolumeGigabytes, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalBackups, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalBackupGigabytes, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalVolumesUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalVolumesUsed, limits.Absolute.MaxTotalVolumes) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalGigabytesUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalGigabytesUsed, limits.Absolute.MaxTotalVolumeGigabytes) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalSnapshotsUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalSnapshotsUsed, limits.Absolute.MaxTotalSnapshots) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalBackupsUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalBackupsUsed, limits.Absolute.MaxTotalBackups) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalBackupGigabytesUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalBackupGigabytesUsed, limits.Absolute.MaxTotalBackupGigabytes) +} diff --git a/acceptance/openstack/evs/extensions/schedulerhints_test.go b/acceptance/openstack/evs/extensions/schedulerhints_test.go new file mode 100644 index 000000000..c0d75b13f --- /dev/null +++ b/acceptance/openstack/evs/extensions/schedulerhints_test.go @@ -0,0 +1,57 @@ +package extensions + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/schedulerhints" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestSchedulerHints(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volumeName := tools.RandomString("ACPTTEST", 16) + createOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + } + + volume1, err := volumes.Create(client, createOpts).Extract() + th.AssertNoErr(t, err) + + err = volumes.WaitForStatus(client, volume1.ID, "available", 60) + th.AssertNoErr(t, err) + defer volumes.Delete(client, volume1.ID, volumes.DeleteOpts{}) + + volumeName = tools.RandomString("ACPTTEST", 16) + base := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + } + + schedulerHints := schedulerhints.SchedulerHints{ + SameHost: []string{ + volume1.ID, + }, + } + + createOptsWithHints := schedulerhints.CreateOptsExt{ + VolumeCreateOptsBuilder: base, + SchedulerHints: schedulerHints, + } + + volume2, err := volumes.Create(client, createOptsWithHints).Extract() + th.AssertNoErr(t, err) + + err = volumes.WaitForStatus(client, volume2.ID, "available", 60) + th.AssertNoErr(t, err) + + err = volumes.Delete(client, volume2.ID, volumes.DeleteOpts{}).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/acceptance/openstack/evs/extensions/schedulerstats_test.go b/acceptance/openstack/evs/extensions/schedulerstats_test.go new file mode 100644 index 000000000..174906c5f --- /dev/null +++ b/acceptance/openstack/evs/extensions/schedulerstats_test.go @@ -0,0 +1,31 @@ +package extensions + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/schedulerstats" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestSchedulerStatsList(t *testing.T) { + clients.RequireAdmin(t) + + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + listOpts := schedulerstats.ListOpts{ + Detail: true, + } + + allPages, err := schedulerstats.List(blockClient, listOpts).AllPages() + th.AssertNoErr(t, err) + + allStats, err := schedulerstats.ExtractStoragePools(allPages) + th.AssertNoErr(t, err) + + for _, stat := range allStats { + tools.PrintResource(t, stat) + } +} diff --git a/acceptance/openstack/evs/extensions/services_test.go b/acceptance/openstack/evs/extensions/services_test.go new file mode 100644 index 000000000..7d7b08a5a --- /dev/null +++ b/acceptance/openstack/evs/extensions/services_test.go @@ -0,0 +1,27 @@ +package extensions + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/services" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestServicesList(t *testing.T) { + clients.RequireAdmin(t) + + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + allPages, err := services.List(blockClient, services.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + allServices, err := services.ExtractServices(allPages) + th.AssertNoErr(t, err) + + for _, service := range allServices { + tools.PrintResource(t, service) + } +} diff --git a/acceptance/openstack/evs/extensions/volumeactions_test.go b/acceptance/openstack/evs/extensions/volumeactions_test.go new file mode 100644 index 000000000..dd7690479 --- /dev/null +++ b/acceptance/openstack/evs/extensions/volumeactions_test.go @@ -0,0 +1,208 @@ +package extensions + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + compute "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/compute/v2" + blockstorage "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/evs/v2" + blockstorageV3 "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/evs/v3" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v2/volumes" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestVolumeActionsUploadImageDestroy(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + computeClient, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + volumeImage, err := CreateUploadImage(t, blockClient, volume) + th.AssertNoErr(t, err) + + tools.PrintResource(t, volumeImage) + + err = DeleteUploadedImage(t, computeClient, volumeImage.ImageID) + th.AssertNoErr(t, err) +} + +func TestVolumeActionsAttachCreateDestroy(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + computeClient, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + server, err := compute.CreateServer(t, computeClient) + th.AssertNoErr(t, err) + defer compute.DeleteServer(t, computeClient, server) + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + err = CreateVolumeAttach(t, blockClient, volume, server) + th.AssertNoErr(t, err) + + newVolume, err := volumes.Get(blockClient, volume.ID).Extract() + th.AssertNoErr(t, err) + + DeleteVolumeAttach(t, blockClient, newVolume) +} + +func TestVolumeActionsReserveUnreserve(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := blockstorage.CreateVolume(t, client) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, client, volume) + + err = CreateVolumeReserve(t, client, volume) + th.AssertNoErr(t, err) + defer DeleteVolumeReserve(t, client, volume) +} + +func TestVolumeActionsExtendSize(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + tools.PrintResource(t, volume) + + err = ExtendVolumeSize(t, blockClient, volume) + th.AssertNoErr(t, err) + + newVolume, err := volumes.Get(blockClient, volume.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newVolume) +} + +func TestVolumeActionsImageMetadata(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + err = SetImageMetadata(t, blockClient, volume) + th.AssertNoErr(t, err) +} + +func TestVolumeActionsSetBootable(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + err = SetBootable(t, blockClient, volume) + th.AssertNoErr(t, err) +} + +func TestVolumeActionsChangeType(t *testing.T) { + // clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volumeType1, err := blockstorageV3.CreateVolumeTypeNoExtraSpecs(t, client) + th.AssertNoErr(t, err) + defer blockstorageV3.DeleteVolumeType(t, client, volumeType1) + + volumeType2, err := blockstorageV3.CreateVolumeTypeNoExtraSpecs(t, client) + th.AssertNoErr(t, err) + defer blockstorageV3.DeleteVolumeType(t, client, volumeType2) + + volume, err := blockstorageV3.CreateVolumeWithType(t, client, volumeType1) + th.AssertNoErr(t, err) + defer blockstorageV3.DeleteVolume(t, client, volume) + + tools.PrintResource(t, volume) + + err = ChangeVolumeType(t, client, volume, volumeType2) + th.AssertNoErr(t, err) + + newVolume, err := volumes.Get(client, volume.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newVolume.VolumeType, volumeType2.Name) + + tools.PrintResource(t, newVolume) +} + +func TestVolumeActionsReImage(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/yoga") + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatal(err) + } + + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + blockClient.Microversion = "3.68" + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + err = ReImage(t, blockClient, volume, choices.ImageID) + th.AssertNoErr(t, err) +} + +// Note(jtopjian): I plan to work on this at some point, but it requires +// setting up a server with iscsi utils. +/* +func TestVolumeConns(t *testing.T) { + client, err := newClient() + th.AssertNoErr(t, err) + + t.Logf("Creating volume") + cv, err := volumes.Create(client, &volumes.CreateOpts{ + Size: 1, + Name: "blockv2-volume", + }).Extract() + th.AssertNoErr(t, err) + + defer func() { + err = volumes.WaitForStatus(client, cv.ID, "available", 60) + th.AssertNoErr(t, err) + + t.Logf("Deleting volume") + err = volumes.Delete(client, cv.ID, volumes.DeleteOpts{}).ExtractErr() + th.AssertNoErr(t, err) + }() + + err = volumes.WaitForStatus(client, cv.ID, "available", 60) + th.AssertNoErr(t, err) + + connOpts := &volumeactions.ConnectorOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: false, + Platform: "x86_64", + OSType: "linux2", + } + + t.Logf("Initializing connection") + _, err = volumeactions.InitializeConnection(client, cv.ID, connOpts).Extract() + th.AssertNoErr(t, err) + + t.Logf("Terminating connection") + err = volumeactions.TerminateConnection(client, cv.ID, connOpts).ExtractErr() + th.AssertNoErr(t, err) +} +*/ diff --git a/acceptance/openstack/evs/extensions/volumetenants_test.go b/acceptance/openstack/evs/extensions/volumetenants_test.go new file mode 100644 index 000000000..6a9fed8e3 --- /dev/null +++ b/acceptance/openstack/evs/extensions/volumetenants_test.go @@ -0,0 +1,44 @@ +package extensions + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + blockstorage "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/evs/v3" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/volumetenants" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestVolumeTenants(t *testing.T) { + type volumeWithTenant struct { + volumes.Volume + volumetenants.VolumeTenantExt + } + + var allVolumes []volumeWithTenant + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + listOpts := volumes.ListOpts{ + Name: "I SHOULD NOT EXIST", + } + allPages, err := volumes.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + err = volumes.ExtractVolumesInto(allPages, &allVolumes) + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, len(allVolumes)) + + volume1, err := blockstorage.CreateVolume(t, client) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, client, volume1) + + allPages, err = volumes.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + err = volumes.ExtractVolumesInto(allPages, &allVolumes) + th.AssertNoErr(t, err) + th.AssertEquals(t, true, len(allVolumes) > 0) +} diff --git a/acceptance/openstack/evs/noauth/blockstorage.go b/acceptance/openstack/evs/noauth/blockstorage.go new file mode 100644 index 000000000..1bf61fef9 --- /dev/null +++ b/acceptance/openstack/evs/noauth/blockstorage.go @@ -0,0 +1,142 @@ +// Package noauth contains common functions for creating block storage based +// resources for use in acceptance tests. See the `*_test.go` files for +// example usages. +package noauth + +import ( + "testing" + + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/snapshots" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" +) + +// CreateVolume will create a volume with a random name and size of 1GB. An +// error will be returned if the volume was unable to be created. +func CreateVolume(t *testing.T, client *golangsdk.ServiceClient) (*volumes.Volume, error) { + if testing.Short() { + t.Skip("Skipping test that requires volume creation in short mode.") + } + + volumeName := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create volume: %s", volumeName) + + createOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + } + + volume, err := volumes.Create(client, createOpts).Extract() + if err != nil { + return volume, err + } + + err = volumes.WaitForStatus(client, volume.ID, "available", 60) + if err != nil { + return volume, err + } + + return volume, nil +} + +// CreateVolumeFromImage will create a volume from with a random name and size of +// 1GB. An error will be returned if the volume was unable to be created. +func CreateVolumeFromImage(t *testing.T, client *golangsdk.ServiceClient) (*volumes.Volume, error) { + if testing.Short() { + t.Skip("Skipping test that requires volume creation in short mode.") + } + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatal(err) + } + + volumeName := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create volume: %s", volumeName) + + createOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + ImageID: choices.ImageID, + } + + volume, err := volumes.Create(client, createOpts).Extract() + if err != nil { + return volume, err + } + + err = volumes.WaitForStatus(client, volume.ID, "available", 60) + if err != nil { + return volume, err + } + + return volume, nil +} + +// DeleteVolume will delete a volume. A fatal error will occur if the volume +// failed to be deleted. This works best when used as a deferred function. +func DeleteVolume(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) { + err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) + } + + t.Logf("Deleted volume: %s", volume.ID) +} + +// CreateSnapshot will create a snapshot of the specified volume. +// Snapshot will be assigned a random name and description. +func CreateSnapshot(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { + if testing.Short() { + t.Skip("Skipping test that requires snapshot creation in short mode.") + } + + snapshotName := tools.RandomString("ACPTTEST", 16) + snapshotDescription := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create snapshot: %s", snapshotName) + + createOpts := snapshots.CreateOpts{ + VolumeID: volume.ID, + Name: snapshotName, + Description: snapshotDescription, + } + + snapshot, err := snapshots.Create(client, createOpts).Extract() + if err != nil { + return snapshot, err + } + + err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) + if err != nil { + return snapshot, err + } + + return snapshot, nil +} + +// DeleteSnapshot will delete a snapshot. A fatal error will occur if the +// snapshot failed to be deleted. +func DeleteSnapshot(t *testing.T, client *golangsdk.ServiceClient, snapshot *snapshots.Snapshot) { + err := snapshots.Delete(client, snapshot.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) + } + + // Volumes can't be deleted until their snapshots have been, + // so block up to 120 seconds for the snapshot to delete. + err = tools.WaitFor(func() (bool, error) { + _, err := snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + return true, nil + } + + return false, nil + }) + if err != nil { + t.Fatalf("Error waiting for snapshot to delete: %v", err) + } + + t.Logf("Deleted snapshot: %s", snapshot.ID) +} diff --git a/acceptance/openstack/evs/noauth/snapshots_test.go b/acceptance/openstack/evs/noauth/snapshots_test.go new file mode 100644 index 000000000..562e8e452 --- /dev/null +++ b/acceptance/openstack/evs/noauth/snapshots_test.go @@ -0,0 +1,56 @@ +package noauth + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/snapshots" +) + +func TestSnapshotsList(t *testing.T) { + client, err := clients.NewBlockStorageV3NoAuthClient() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve snapshots: %v", err) + } + + allSnapshots, err := snapshots.ExtractSnapshots(allPages) + if err != nil { + t.Fatalf("Unable to extract snapshots: %v", err) + } + + for _, snapshot := range allSnapshots { + tools.PrintResource(t, snapshot) + } +} + +func TestSnapshotsCreateDelete(t *testing.T) { + client, err := clients.NewBlockStorageV3NoAuthClient() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + volume, err := CreateVolume(t, client) + if err != nil { + t.Fatalf("Unable to create volume: %v", err) + } + defer DeleteVolume(t, client, volume) + + snapshot, err := CreateSnapshot(t, client, volume) + if err != nil { + t.Fatalf("Unable to create snapshot: %v", err) + } + defer DeleteSnapshot(t, client, snapshot) + + newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve snapshot: %v", err) + } + + tools.PrintResource(t, newSnapshot) +} diff --git a/acceptance/openstack/evs/noauth/volumes_test.go b/acceptance/openstack/evs/noauth/volumes_test.go new file mode 100644 index 000000000..6afd4f4ed --- /dev/null +++ b/acceptance/openstack/evs/noauth/volumes_test.go @@ -0,0 +1,50 @@ +package noauth + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" +) + +func TestVolumesList(t *testing.T) { + client, err := clients.NewBlockStorageV3NoAuthClient() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve volumes: %v", err) + } + + allVolumes, err := volumes.ExtractVolumes(allPages) + if err != nil { + t.Fatalf("Unable to extract volumes: %v", err) + } + + for _, volume := range allVolumes { + tools.PrintResource(t, volume) + } +} + +func TestVolumesCreateDestroy(t *testing.T) { + client, err := clients.NewBlockStorageV3NoAuthClient() + if err != nil { + t.Fatalf("Unable to create blockstorage client: %v", err) + } + + volume, err := CreateVolume(t, client) + if err != nil { + t.Fatalf("Unable to create volume: %v", err) + } + defer DeleteVolume(t, client, volume) + + newVolume, err := volumes.Get(client, volume.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve volume: %v", err) + } + + tools.PrintResource(t, newVolume) +} diff --git a/acceptance/openstack/evs/v3/blockstorage.go b/acceptance/openstack/evs/v3/blockstorage.go new file mode 100644 index 000000000..d75962f3b --- /dev/null +++ b/acceptance/openstack/evs/v3/blockstorage.go @@ -0,0 +1,321 @@ +// Package v3 contains common functions for creating block storage based +// resources for use in acceptance tests. See the `*_test.go` files for +// example usages. +package v3 + +import ( + "testing" + + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/qos" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/snapshots" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumetypes" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +// CreateSnapshot will create a snapshot of the specified volume. +// Snapshot will be assigned a random name and description. +func CreateSnapshot(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { + snapshotName := tools.RandomString("ACPTTEST", 16) + snapshotDescription := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create snapshot: %s", snapshotName) + + createOpts := snapshots.CreateOpts{ + VolumeID: volume.ID, + Name: snapshotName, + Description: snapshotDescription, + } + + snapshot, err := snapshots.Create(client, createOpts).Extract() + if err != nil { + return snapshot, err + } + + err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) + if err != nil { + return snapshot, err + } + + tools.PrintResource(t, snapshot) + th.AssertEquals(t, snapshot.Name, snapshotName) + th.AssertEquals(t, snapshot.VolumeID, volume.ID) + + t.Logf("Successfully created snapshot: %s", snapshot.ID) + + return snapshot, nil +} + +// CreateVolume will create a volume with a random name and size of 1GB. An +// error will be returned if the volume was unable to be created. +func CreateVolume(t *testing.T, client *golangsdk.ServiceClient) (*volumes.Volume, error) { + volumeName := tools.RandomString("ACPTTEST", 16) + volumeDescription := tools.RandomString("ACPTTEST-DESC", 16) + t.Logf("Attempting to create volume: %s", volumeName) + + createOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + Description: volumeDescription, + } + + volume, err := volumes.Create(client, createOpts).Extract() + if err != nil { + return volume, err + } + + err = volumes.WaitForStatus(client, volume.ID, "available", 60) + if err != nil { + return volume, err + } + + tools.PrintResource(t, volume) + th.AssertEquals(t, volume.Name, volumeName) + th.AssertEquals(t, volume.Description, volumeDescription) + th.AssertEquals(t, volume.Size, 1) + + t.Logf("Successfully created volume: %s", volume.ID) + + return volume, nil +} + +// CreateVolumeWithType will create a volume of the given volume type +// with a random name and size of 1GB. An error will be returned if +// the volume was unable to be created. +func CreateVolumeWithType(t *testing.T, client *golangsdk.ServiceClient, vt *volumetypes.VolumeType) (*volumes.Volume, error) { + volumeName := tools.RandomString("ACPTTEST", 16) + volumeDescription := tools.RandomString("ACPTTEST-DESC", 16) + t.Logf("Attempting to create volume: %s", volumeName) + + createOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + Description: volumeDescription, + VolumeType: vt.Name, + } + + volume, err := volumes.Create(client, createOpts).Extract() + if err != nil { + return volume, err + } + + err = volumes.WaitForStatus(client, volume.ID, "available", 60) + if err != nil { + return volume, err + } + + tools.PrintResource(t, volume) + th.AssertEquals(t, volume.Name, volumeName) + th.AssertEquals(t, volume.Description, volumeDescription) + th.AssertEquals(t, volume.Size, 1) + th.AssertEquals(t, volume.VolumeType, vt.Name) + + t.Logf("Successfully created volume: %s", volume.ID) + + return volume, nil +} + +// CreateVolumeType will create a volume type with a random name. An +// error will be returned if the volume was unable to be created. +func CreateVolumeType(t *testing.T, client *golangsdk.ServiceClient) (*volumetypes.VolumeType, error) { + name := tools.RandomString("ACPTTEST", 16) + description := "create_from_gophercloud" + t.Logf("Attempting to create volume type: %s", name) + + createOpts := volumetypes.CreateOpts{ + Name: name, + ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name"}, + Description: description, + } + + vt, err := volumetypes.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, vt) + th.AssertEquals(t, vt.IsPublic, true) + th.AssertEquals(t, vt.Name, name) + th.AssertEquals(t, vt.Description, description) + // TODO: For some reason returned extra_specs are empty even in API reference: https://developer.openstack.org/api-ref/block-storage/v3/?expanded=create-a-volume-type-detail#volume-types-types + // "extra_specs": {} + // th.AssertEquals(t, vt.ExtraSpecs, createOpts.ExtraSpecs) + + t.Logf("Successfully created volume type: %s", vt.ID) + + return vt, nil +} + +// CreateVolumeTypeNoExtraSpecs will create a volume type with a random name and +// no extra specs. This is required to bypass cinder-scheduler filters and be able +// to create a volume with this volumeType. An error will be returned if the volume +// type was unable to be created. +func CreateVolumeTypeNoExtraSpecs(t *testing.T, client *golangsdk.ServiceClient) (*volumetypes.VolumeType, error) { + name := tools.RandomString("ACPTTEST", 16) + description := "create_from_gophercloud" + t.Logf("Attempting to create volume type: %s", name) + + createOpts := volumetypes.CreateOpts{ + Name: name, + ExtraSpecs: map[string]string{}, + Description: description, + } + + vt, err := volumetypes.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, vt) + th.AssertEquals(t, vt.IsPublic, true) + th.AssertEquals(t, vt.Name, name) + th.AssertEquals(t, vt.Description, description) + + t.Logf("Successfully created volume type: %s", vt.ID) + + return vt, nil +} + +// CreatePrivateVolumeType will create a private volume type with a random +// name and no extra specs. An error will be returned if the volume type was +// unable to be created. +func CreatePrivateVolumeType(t *testing.T, client *golangsdk.ServiceClient) (*volumetypes.VolumeType, error) { + name := tools.RandomString("ACPTTEST", 16) + description := "create_from_gophercloud" + isPublic := false + t.Logf("Attempting to create volume type: %s", name) + + createOpts := volumetypes.CreateOpts{ + Name: name, + ExtraSpecs: map[string]string{}, + Description: description, + IsPublic: &isPublic, + } + + vt, err := volumetypes.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, vt) + th.AssertEquals(t, vt.IsPublic, false) + th.AssertEquals(t, vt.Name, name) + th.AssertEquals(t, vt.Description, description) + + t.Logf("Successfully created volume type: %s", vt.ID) + + return vt, nil +} + +// DeleteSnapshot will delete a snapshot. A fatal error will occur if the +// snapshot failed to be deleted. +func DeleteSnapshot(t *testing.T, client *golangsdk.ServiceClient, snapshot *snapshots.Snapshot) { + err := snapshots.Delete(client, snapshot.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) + } + + // Volumes can't be deleted until their snapshots have been, + // so block until the snapshoth as been deleted. + err = tools.WaitFor(func() (bool, error) { + _, err := snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + return true, nil + } + + return false, nil + }) + if err != nil { + t.Fatalf("Error waiting for snapshot to delete: %v", err) + } + + t.Logf("Deleted snapshot: %s", snapshot.ID) +} + +// DeleteVolume will delete a volume. A fatal error will occur if the volume +// failed to be deleted. This works best when used as a deferred function. +func DeleteVolume(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) { + t.Logf("Attempting to delete volume: %s", volume.ID) + + err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) + } + + // VolumeTypes can't be deleted until their volumes have been, + // so block until the volume is deleted. + err = tools.WaitFor(func() (bool, error) { + _, err := volumes.Get(client, volume.ID).Extract() + if err != nil { + return true, nil + } + + return false, nil + }) + if err != nil { + t.Fatalf("Error waiting for volume to delete: %v", err) + } + + t.Logf("Successfully deleted volume: %s", volume.ID) +} + +// DeleteVolumeType will delete a volume type. A fatal error will occur if the +// volume type failed to be deleted. This works best when used as a deferred +// function. +func DeleteVolumeType(t *testing.T, client *golangsdk.ServiceClient, vt *volumetypes.VolumeType) { + t.Logf("Attempting to delete volume type: %s", vt.ID) + + err := volumetypes.Delete(client, vt.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete volume %s: %v", vt.ID, err) + } + + t.Logf("Successfully deleted volume type: %s", vt.ID) +} + +// CreateQoS will create a QoS with one spec and a random name. An +// error will be returned if the volume was unable to be created. +func CreateQoS(t *testing.T, client *golangsdk.ServiceClient) (*qos.QoS, error) { + name := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create QoS: %s", name) + + createOpts := qos.CreateOpts{ + Name: name, + Consumer: qos.ConsumerFront, + Specs: map[string]string{ + "read_iops_sec": "20000", + }, + } + + qs, err := qos.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, qs) + th.AssertEquals(t, qs.Consumer, "front-end") + th.AssertEquals(t, qs.Name, name) + th.AssertDeepEquals(t, qs.Specs, createOpts.Specs) + + t.Logf("Successfully created QoS: %s", qs.ID) + + return qs, nil +} + +// DeleteQoS will delete a QoS. A fatal error will occur if the QoS +// failed to be deleted. This works best when used as a deferred function. +func DeleteQoS(t *testing.T, client *golangsdk.ServiceClient, qs *qos.QoS) { + t.Logf("Attempting to delete QoS: %s", qs.ID) + + deleteOpts := qos.DeleteOpts{ + Force: true, + } + + err := qos.Delete(client, qs.ID, deleteOpts).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete QoS %s: %v", qs.ID, err) + } + + t.Logf("Successfully deleted QoS: %s", qs.ID) +} diff --git a/acceptance/openstack/evs/v3/qos_test.go b/acceptance/openstack/evs/v3/qos_test.go new file mode 100644 index 000000000..fc4540ca8 --- /dev/null +++ b/acceptance/openstack/evs/v3/qos_test.go @@ -0,0 +1,127 @@ +package v3 + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/qos" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestQoS(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + qos1, err := CreateQoS(t, client) + th.AssertNoErr(t, err) + defer DeleteQoS(t, client, qos1) + + qos2, err := CreateQoS(t, client) + th.AssertNoErr(t, err) + defer DeleteQoS(t, client, qos2) + + getQoS2, err := qos.Get(client, qos2.ID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, qos2, getQoS2) + + err = qos.DeleteKeys(client, qos2.ID, qos.DeleteKeysOpts{"read_iops_sec"}).ExtractErr() + th.AssertNoErr(t, err) + + updateOpts := qos.UpdateOpts{ + Consumer: qos.ConsumerBack, + Specs: map[string]string{ + "read_iops_sec": "40000", + "write_iops_sec": "40000", + }, + } + + expectedQosSpecs := map[string]string{ + "consumer": "back-end", + "read_iops_sec": "40000", + "write_iops_sec": "40000", + } + + updatedQosSpecs, err := qos.Update(client, qos2.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, updatedQosSpecs, expectedQosSpecs) + + listOpts := qos.ListOpts{ + Limit: 1, + } + + err = qos.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + actual, err := qos.ExtractQoS(page) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(actual)) + + var found bool + for _, q := range actual { + if q.ID == qos1.ID || q.ID == qos2.ID { + found = true + } + } + + th.AssertEquals(t, found, true) + + return true, nil + }) + + th.AssertNoErr(t, err) + +} + +func TestQoSAssociations(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + qos1, err := CreateQoS(t, client) + th.AssertNoErr(t, err) + defer DeleteQoS(t, client, qos1) + + vt, err := CreateVolumeType(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, vt) + + associateOpts := qos.AssociateOpts{ + VolumeTypeID: vt.ID, + } + + err = qos.Associate(client, qos1.ID, associateOpts).ExtractErr() + th.AssertNoErr(t, err) + + allQosAssociations, err := qos.ListAssociations(client, qos1.ID).AllPages() + th.AssertNoErr(t, err) + + allAssociations, err := qos.ExtractAssociations(allQosAssociations) + th.AssertNoErr(t, err) + tools.PrintResource(t, allAssociations) + th.AssertEquals(t, 1, len(allAssociations)) + th.AssertEquals(t, vt.ID, allAssociations[0].ID) + + disassociateOpts := qos.DisassociateOpts{ + VolumeTypeID: vt.ID, + } + + err = qos.Disassociate(client, qos1.ID, disassociateOpts).ExtractErr() + th.AssertNoErr(t, err) + + allQosAssociations, err = qos.ListAssociations(client, qos1.ID).AllPages() + th.AssertNoErr(t, err) + + allAssociations, err = qos.ExtractAssociations(allQosAssociations) + th.AssertNoErr(t, err) + tools.PrintResource(t, allAssociations) + th.AssertEquals(t, 0, len(allAssociations)) + + err = qos.Associate(client, qos1.ID, associateOpts).ExtractErr() + th.AssertNoErr(t, err) + + err = qos.DisassociateAll(client, qos1.ID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/acceptance/openstack/evs/v3/quotaset_test.go b/acceptance/openstack/evs/v3/quotaset_test.go new file mode 100644 index 000000000..5d8948655 --- /dev/null +++ b/acceptance/openstack/evs/v3/quotaset_test.go @@ -0,0 +1,187 @@ +package v3 + +import ( + "os" + "testing" + + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/quotasets" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumetypes" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestQuotasetGet(t *testing.T) { + clients.RequireAdmin(t) + + client, projectID := getClientAndProject(t) + + quotaSet, err := quotasets.Get(client, projectID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaSet) +} + +func TestQuotasetGetDefaults(t *testing.T) { + clients.RequireAdmin(t) + + client, projectID := getClientAndProject(t) + + quotaSet, err := quotasets.GetDefaults(client, projectID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaSet) +} + +func TestQuotasetGetUsage(t *testing.T) { + clients.RequireAdmin(t) + + client, projectID := getClientAndProject(t) + + quotaSetUsage, err := quotasets.GetUsage(client, projectID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaSetUsage) +} + +var UpdateQuotaOpts = quotasets.UpdateOpts{ + Volumes: golangsdk.IntToPointer(100), + Snapshots: golangsdk.IntToPointer(200), + Gigabytes: golangsdk.IntToPointer(300), + PerVolumeGigabytes: golangsdk.IntToPointer(50), + Backups: golangsdk.IntToPointer(2), + BackupGigabytes: golangsdk.IntToPointer(300), + Groups: golangsdk.IntToPointer(350), + Extra: map[string]interface{}{ + "volumes_foo": golangsdk.IntToPointer(100), + }, +} + +var UpdatedQuotas = quotasets.QuotaSet{ + Volumes: 100, + Snapshots: 200, + Gigabytes: 300, + PerVolumeGigabytes: 50, + Backups: 2, + BackupGigabytes: 300, + Groups: 350, +} + +var VolumeTypeIsPublic = true +var VolumeTypeCreateOpts = volumetypes.CreateOpts{ + Name: "foo", + IsPublic: &VolumeTypeIsPublic, + Description: "foo", + ExtraSpecs: map[string]string{}, +} + +func TestQuotasetUpdate(t *testing.T) { + clients.RequireAdmin(t) + + client, projectID := getClientAndProject(t) + + // save original quotas + orig, err := quotasets.Get(client, projectID).Extract() + th.AssertNoErr(t, err) + + // create volumeType to test volume type quota + volumeType, err := volumetypes.Create(client, VolumeTypeCreateOpts).Extract() + th.AssertNoErr(t, err) + + defer func() { + restore := quotasets.UpdateOpts{} + FillUpdateOptsFromQuotaSet(*orig, &restore) + + err := volumetypes.Delete(client, volumeType.ID).ExtractErr() + th.AssertNoErr(t, err) + + _, err = quotasets.Update(client, projectID, restore).Extract() + th.AssertNoErr(t, err) + + }() + + // test Update + resultQuotas, err := quotasets.Update(client, projectID, UpdateQuotaOpts).Extract() + th.AssertNoErr(t, err) + + // We dont know the default quotas, so just check if the quotas are not the + // same as before + newQuotas, err := quotasets.Get(client, projectID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, resultQuotas.Volumes, newQuotas.Volumes) + th.AssertEquals(t, resultQuotas.Extra["volumes_foo"], newQuotas.Extra["volumes_foo"]) + + // test that resultQuotas.Extra is populated with the 3 new quota types + // for the new volumeType foo, don't take into account other volume types + count := 0 + for k, _ := range resultQuotas.Extra { + tools.PrintResource(t, k) + switch k { + case + "volumes_foo", + "snapshots_foo", + "gigabytes_foo": + count += 1 + } + } + + th.AssertEquals(t, count, 3) + + // unpopulate resultQuotas.Extra as it is different per cloud and test + // rest of the quotaSet + resultQuotas.Extra = map[string]interface{}(nil) + th.AssertDeepEquals(t, UpdatedQuotas, *resultQuotas) +} + +func TestQuotasetDelete(t *testing.T) { + clients.RequireAdmin(t) + + client, projectID := getClientAndProject(t) + + // save original quotas + orig, err := quotasets.Get(client, projectID).Extract() + th.AssertNoErr(t, err) + + defer func() { + restore := quotasets.UpdateOpts{} + FillUpdateOptsFromQuotaSet(*orig, &restore) + + _, err = quotasets.Update(client, projectID, restore).Extract() + th.AssertNoErr(t, err) + }() + + // Obtain environment default quotaset values to validate deletion. + defaultQuotaSet, err := quotasets.GetDefaults(client, projectID).Extract() + th.AssertNoErr(t, err) + + // Test Delete + err = quotasets.Delete(client, projectID).ExtractErr() + th.AssertNoErr(t, err) + + newQuotas, err := quotasets.Get(client, projectID).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, newQuotas.Volumes, defaultQuotaSet.Volumes) +} + +// getClientAndProject reduces boilerplate by returning a new blockstorage v3 +// ServiceClient and a project ID obtained from the OS_PROJECT_NAME envvar. +func getClientAndProject(t *testing.T) (*golangsdk.ServiceClient, string) { + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + projectID := os.Getenv("OS_PROJECT_NAME") + th.AssertNoErr(t, err) + return client, projectID +} + +func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) { + dest.Volumes = &src.Volumes + dest.Snapshots = &src.Snapshots + dest.Gigabytes = &src.Gigabytes + dest.PerVolumeGigabytes = &src.PerVolumeGigabytes + dest.Backups = &src.Backups + dest.BackupGigabytes = &src.BackupGigabytes + dest.Groups = &src.Groups +} diff --git a/acceptance/openstack/evs/v3/snapshots_test.go b/acceptance/openstack/evs/v3/snapshots_test.go new file mode 100644 index 000000000..d8eb3abe9 --- /dev/null +++ b/acceptance/openstack/evs/v3/snapshots_test.go @@ -0,0 +1,72 @@ +package v3 + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/snapshots" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestSnapshots(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume1, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer DeleteVolume(t, client, volume1) + + snapshot1, err := CreateSnapshot(t, client, volume1) + th.AssertNoErr(t, err) + defer DeleteSnapshot(t, client, snapshot1) + + // Update snapshot + updatedSnapshotName := tools.RandomString("ACPTTEST", 16) + updatedSnapshotDescription := tools.RandomString("ACPTTEST", 16) + updateOpts := snapshots.UpdateOpts{ + Name: &updatedSnapshotName, + Description: &updatedSnapshotDescription, + } + t.Logf("Attempting to update snapshot: %s", updatedSnapshotName) + updatedSnapshot, err := snapshots.Update(client, snapshot1.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedSnapshot) + th.AssertEquals(t, updatedSnapshot.Name, updatedSnapshotName) + th.AssertEquals(t, updatedSnapshot.Description, updatedSnapshotDescription) + + volume2, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer DeleteVolume(t, client, volume2) + + snapshot2, err := CreateSnapshot(t, client, volume2) + th.AssertNoErr(t, err) + defer DeleteSnapshot(t, client, snapshot2) + + listOpts := snapshots.ListOpts{ + Limit: 1, + } + + err = snapshots.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + actual, err := snapshots.ExtractSnapshots(page) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(actual)) + + var found bool + for _, v := range actual { + if v.ID == snapshot1.ID || v.ID == snapshot2.ID { + found = true + } + } + + th.AssertEquals(t, found, true) + + return true, nil + }) + + th.AssertNoErr(t, err) +} diff --git a/acceptance/openstack/evs/v3/volumeattachments.go b/acceptance/openstack/evs/v3/volumeattachments.go new file mode 100644 index 000000000..6c8c53d30 --- /dev/null +++ b/acceptance/openstack/evs/v3/volumeattachments.go @@ -0,0 +1,110 @@ +package v3 + +import ( + "fmt" + "testing" + + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/servers" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/attachments" + v3 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" +) + +// CreateVolumeAttachment will attach a volume to an instance. An error will be +// returned if the attachment failed. +func CreateVolumeAttachment(t *testing.T, client *golangsdk.ServiceClient, volume *v3.Volume, server *servers.Server) error { + if testing.Short() { + t.Skip("Skipping test that requires volume attachment in short mode.") + } + + attachOpts := &attachments.CreateOpts{ + VolumeUUID: volume.ID, + InstanceUUID: server.ID, + Connector: map[string]interface{}{ + "mode": "rw", + "initiator": "fake", + }, + } + + t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) + + var err error + var attachment *attachments.Attachment + if attachment, err = attachments.Create(client, attachOpts).Extract(); err != nil { + return err + } + + mv := client.Microversion + client.Microversion = "3.44" + defer func() { + client.Microversion = mv + }() + if err = attachments.Complete(client, attachment.ID).ExtractErr(); err != nil { + return err + } + + if err = attachments.WaitForStatus(client, attachment.ID, "attached", 60); err != nil { + e := attachments.Delete(client, attachment.ID).ExtractErr() + if e != nil { + t.Logf("Failed to delete %q attachment: %s", attachment.ID, err) + } + return err + } + + attachment, err = attachments.Get(client, attachment.ID).Extract() + if err != nil { + return err + } + + /* + // Not clear how perform a proper update, OpenStack returns "Unable to update the attachment." + updateOpts := &attachments.UpdateOpts{ + Connector: map[string]interface{}{ + "mode": "ro", + "initiator": "fake", + }, + } + attachment, err = attachments.Update(client, attachment.ID, updateOpts).Extract() + if err != nil { + return err + } + */ + + listOpts := &attachments.ListOpts{ + VolumeID: volume.ID, + InstanceID: server.ID, + } + allPages, err := attachments.List(client, listOpts).AllPages() + if err != nil { + return err + } + + allAttachments, err := attachments.ExtractAttachments(allPages) + if err != nil { + return err + } + + if allAttachments[0].ID != attachment.ID { + return fmt.Errorf("Attachment IDs from get and list are not equal: %q != %q", allAttachments[0].ID, attachment.ID) + } + + t.Logf("Attached volume %s to server %s within %q attachment", volume.ID, server.ID, attachment.ID) + + return nil +} + +// DeleteVolumeAttachment will detach a volume from an instance. A fatal error +// will occur if the attachment failed to be deleted. +func DeleteVolumeAttachment(t *testing.T, client *golangsdk.ServiceClient, volume *v3.Volume) { + t.Logf("Attepting to detach volume volume: %s", volume.ID) + + if err := attachments.Delete(client, volume.Attachments[0].AttachmentID).ExtractErr(); err != nil { + t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) + } + + if err := v3.WaitForStatus(client, volume.ID, "available", 60); err != nil { + t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) + } + + t.Logf("Detached volume: %s", volume.ID) +} diff --git a/acceptance/openstack/evs/v3/volumeattachments_test.go b/acceptance/openstack/evs/v3/volumeattachments_test.go new file mode 100644 index 000000000..b6c2a711a --- /dev/null +++ b/acceptance/openstack/evs/v3/volumeattachments_test.go @@ -0,0 +1,37 @@ +package v3 + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + compute "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/compute/v2" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestVolumeAttachments(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + // minimu required microversion for volume attachments is 3.27 + blockClient.Microversion = "3.27" + + computeClient, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + server, err := compute.CreateServer(t, computeClient) + th.AssertNoErr(t, err) + defer compute.DeleteServer(t, computeClient, server) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + err = CreateVolumeAttachment(t, blockClient, volume, server) + th.AssertNoErr(t, err) + + newVolume, err := volumes.Get(blockClient, volume.ID).Extract() + th.AssertNoErr(t, err) + + DeleteVolumeAttachment(t, blockClient, newVolume) +} diff --git a/acceptance/openstack/evs/v3/volumes_test.go b/acceptance/openstack/evs/v3/volumes_test.go new file mode 100644 index 000000000..14957a08e --- /dev/null +++ b/acceptance/openstack/evs/v3/volumes_test.go @@ -0,0 +1,142 @@ +package v3 + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/snapshots" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestVolumes(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume1, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer DeleteVolume(t, client, volume1) + + volume2, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer DeleteVolume(t, client, volume2) + + // Update volume + updatedVolumeName := "" + updatedVolumeDescription := "" + updateOpts := volumes.UpdateOpts{ + Name: &updatedVolumeName, + Description: &updatedVolumeDescription, + } + updatedVolume, err := volumes.Update(client, volume1.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedVolume) + th.AssertEquals(t, updatedVolume.Name, updatedVolumeName) + th.AssertEquals(t, updatedVolume.Description, updatedVolumeDescription) + + listOpts := volumes.ListOpts{ + Limit: 1, + } + + err = volumes.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + actual, err := volumes.ExtractVolumes(page) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(actual)) + + var found bool + for _, v := range actual { + if v.ID == volume1.ID || v.ID == volume2.ID { + found = true + } + } + + th.AssertEquals(t, found, true) + + return true, nil + }) + + th.AssertNoErr(t, err) +} + +func TestVolumesMultiAttach(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volumeName := tools.RandomString("ACPTTEST", 16) + + volOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + Description: "Testing creation of multiattach enabled volume", + Multiattach: true, + } + + vol, err := volumes.Create(client, volOpts).Extract() + th.AssertNoErr(t, err) + + err = volumes.WaitForStatus(client, vol.ID, "available", 60) + th.AssertNoErr(t, err) + + th.AssertEquals(t, vol.Multiattach, true) + + err = volumes.Delete(client, vol.ID, volumes.DeleteOpts{}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestVolumesCascadeDelete(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + vol, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + + err = volumes.WaitForStatus(client, vol.ID, "available", 60) + th.AssertNoErr(t, err) + + snapshot1, err := CreateSnapshot(t, client, vol) + th.AssertNoErr(t, err) + + snapshot2, err := CreateSnapshot(t, client, vol) + th.AssertNoErr(t, err) + + t.Logf("Attempting to delete volume: %s", vol.ID) + + deleteOpts := volumes.DeleteOpts{Cascade: true} + err = volumes.Delete(client, vol.ID, deleteOpts).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete volume %s: %v", vol.ID, err) + } + + for _, sid := range []string{snapshot1.ID, snapshot2.ID} { + err := tools.WaitFor(func() (bool, error) { + _, err := snapshots.Get(client, sid).Extract() + if err != nil { + return true, nil + } + return false, nil + }) + th.AssertNoErr(t, err) + t.Logf("Successfully deleted snapshot: %s", sid) + } + + err = tools.WaitFor(func() (bool, error) { + _, err := volumes.Get(client, vol.ID).Extract() + if err != nil { + return true, nil + } + return false, nil + }) + th.AssertNoErr(t, err) + + t.Logf("Successfully deleted volume: %s", vol.ID) + +} diff --git a/acceptance/openstack/evs/v3/volumetypes_test.go b/acceptance/openstack/evs/v3/volumetypes_test.go new file mode 100644 index 000000000..f518356d3 --- /dev/null +++ b/acceptance/openstack/evs/v3/volumetypes_test.go @@ -0,0 +1,162 @@ +package v3 + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + identity "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/identity/v3" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumetypes" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestVolumeTypes(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + vt, err := CreateVolumeType(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, vt) + + allPages, err := volumetypes.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allVolumeTypes { + tools.PrintResource(t, v) + if v.ID == vt.ID { + found = true + } + } + + th.AssertEquals(t, found, true) + + isPublic := false + name := vt.Name + "-UPDATED" + description := "" + updateOpts := volumetypes.UpdateOpts{ + Name: &name, + Description: &description, + IsPublic: &isPublic, + } + + newVT, err := volumetypes.Update(client, vt.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newVT) + th.AssertEquals(t, name, newVT.Name) + th.AssertEquals(t, description, newVT.Description) + th.AssertEquals(t, isPublic, newVT.IsPublic) +} + +func TestVolumeTypesExtraSpecs(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + vt, err := CreateVolumeTypeNoExtraSpecs(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, vt) + + createOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "gpu", + "volume_backend_name": "ssd", + } + + createdExtraSpecs, err := volumetypes.CreateExtraSpecs(client, vt.ID, createOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, createdExtraSpecs) + + th.AssertEquals(t, len(createdExtraSpecs), 2) + th.AssertEquals(t, createdExtraSpecs["capabilities"], "gpu") + th.AssertEquals(t, createdExtraSpecs["volume_backend_name"], "ssd") + + err = volumetypes.DeleteExtraSpec(client, vt.ID, "volume_backend_name").ExtractErr() + th.AssertNoErr(t, err) + + updateOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "gpu-2", + } + updatedExtraSpec, err := volumetypes.UpdateExtraSpec(client, vt.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedExtraSpec) + + th.AssertEquals(t, updatedExtraSpec["capabilities"], "gpu-2") + + allExtraSpecs, err := volumetypes.ListExtraSpecs(client, vt.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, allExtraSpecs) + + th.AssertEquals(t, len(allExtraSpecs), 1) + th.AssertEquals(t, allExtraSpecs["capabilities"], "gpu-2") + + singleSpec, err := volumetypes.GetExtraSpec(client, vt.ID, "capabilities").Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, singleSpec) + + th.AssertEquals(t, singleSpec["capabilities"], "gpu-2") +} + +func TestVolumeTypesAccess(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + identityClient, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + vt, err := CreatePrivateVolumeType(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, vt) + + project, err := identity.CreateProject(t, identityClient, nil) + th.AssertNoErr(t, err) + defer identity.DeleteProject(t, identityClient, project.ID) + + addAccessOpts := volumetypes.AddAccessOpts{ + Project: project.ID, + } + + err = volumetypes.AddAccess(client, vt.ID, addAccessOpts).ExtractErr() + th.AssertNoErr(t, err) + + allPages, err := volumetypes.ListAccesses(client, vt.ID).AllPages() + th.AssertNoErr(t, err) + + accessList, err := volumetypes.ExtractAccesses(allPages) + th.AssertNoErr(t, err) + + tools.PrintResource(t, accessList) + + th.AssertEquals(t, len(accessList), 1) + th.AssertEquals(t, accessList[0].ProjectID, project.ID) + th.AssertEquals(t, accessList[0].VolumeTypeID, vt.ID) + + removeAccessOpts := volumetypes.RemoveAccessOpts{ + Project: project.ID, + } + + err = volumetypes.RemoveAccess(client, vt.ID, removeAccessOpts).ExtractErr() + th.AssertNoErr(t, err) + + allPages, err = volumetypes.ListAccesses(client, vt.ID).AllPages() + th.AssertNoErr(t, err) + + accessList, err = volumetypes.ExtractAccesses(allPages) + th.AssertNoErr(t, err) + + tools.PrintResource(t, accessList) + + th.AssertEquals(t, len(accessList), 0) +} diff --git a/openstack/blockstorage/v2/snapshots/requests.go b/openstack/blockstorage/v2/snapshots/requests.go deleted file mode 100644 index 6ada877af..000000000 --- a/openstack/blockstorage/v2/snapshots/requests.go +++ /dev/null @@ -1,167 +0,0 @@ -package snapshots - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// CreateOpts contains options for creating a Snapshot. This object is passed to -// the snapshots.Create function. For more information about these parameters, -// see the Snapshot object. -type CreateOpts struct { - VolumeID string `json:"volume_id" required:"true"` - Force bool `json:"force,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// Create will create a new Snapshot based on the values in CreateOpts. To -// extract the Snapshot object from the response, call the Extract method on the -// CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOpts) (r CreateResult) { - b, err := golangsdk.BuildRequestBody(opts, "snapshot") - if err != nil { - r.Err = err - return - } - - _, r.Err = client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - return -} - -// Delete will delete the existing Snapshot with the provided ID. -func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) - return -} - -// Get retrieves the Snapshot with the provided ID. To extract the Snapshot -// object from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToSnapshotListQuery() (string, error) -} - -// ListOpts hold options for listing Snapshots. It is passed to the -// snapshots.List function. -type ListOpts struct { - // AllTenants will retrieve snapshots of all tenants/projects. - AllTenants bool `q:"all_tenants"` - - // Name will filter by the specified snapshot name. - Name string `q:"name"` - - // Status will filter by the specified status. - Status string `q:"status"` - - // TenantID will filter by a specific tenant/project ID. - // Setting AllTenants is required to use this. - TenantID string `q:"project_id"` - - // VolumeID will filter by a specified volume ID. - VolumeID string `q:"volume_id"` -} - -// ToSnapshotListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToSnapshotListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - if err != nil { - return "", err - } - return q.String(), err -} - -// List returns Snapshots optionally limited by the conditions provided in -// ListOpts. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToSnapshotListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return SnapshotPage{pagination.SinglePageBase(r)} - }) -} - -// UpdateMetadataOptsBuilder allows extensions to add additional parameters to -// the Update request. -type UpdateMetadataOptsBuilder interface { - ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) -} - -// UpdateMetadataOpts contain options for updating an existing Snapshot. This -// object is passed to the snapshots.Update function. For more information -// about the parameters, see the Snapshot object. -type UpdateMetadataOpts struct { - Metadata map[string]interface{} `json:"metadata,omitempty"` -} - -// ToSnapshotUpdateMetadataMap assembles a request body based on the contents of -// an UpdateMetadataOpts. -func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "") -} - -// UpdateMetadata will update the Snapshot with provided information. To -// extract the updated Snapshot from the response, call the ExtractMetadata -// method on the UpdateMetadataResult. -func UpdateMetadata(client *golangsdk.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { - b, err := opts.ToSnapshotUpdateMetadataMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - return -} - -// IDFromName is a convienience function that returns a snapshot's ID given its name. -func IDFromName(client *golangsdk.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractSnapshots(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", golangsdk.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} - case 1: - return id, nil - default: - return "", golangsdk.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"} - } -} diff --git a/openstack/blockstorage/v2/snapshots/results.go b/openstack/blockstorage/v2/snapshots/results.go deleted file mode 100644 index a43198651..000000000 --- a/openstack/blockstorage/v2/snapshots/results.go +++ /dev/null @@ -1,117 +0,0 @@ -package snapshots - -import ( - "encoding/json" - "time" - - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/common/metadata" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// Snapshot contains all the information associated with a Cinder Snapshot. -type Snapshot struct { - // Unique identifier. - ID string `json:"id"` - - // Date created. - CreatedAt time.Time `json:"-"` - - // Date updated. - UpdatedAt time.Time `json:"-"` - - // Display name. - Name string `json:"name"` - - // Display description. - Description string `json:"description"` - - // ID of the Volume from which this Snapshot was created. - VolumeID string `json:"volume_id"` - - // Currect status of the Snapshot. - Status string `json:"status"` - - // Size of the Snapshot, in GB. - Size int `json:"size"` - - // User-defined key-value pairs. - Metadata map[string]string `json:"metadata"` -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - golangsdk.ErrResult -} - -// SnapshotPage is a pagination.Pager that is returned from a call to the List function. -type SnapshotPage struct { - pagination.SinglePageBase -} - -func (r *Snapshot) UnmarshalJSON(b []byte) error { - type tmp Snapshot - var s struct { - tmp - CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Snapshot(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) - - return err -} - -// IsEmpty returns true if a SnapshotPage contains no Snapshots. -func (r SnapshotPage) IsEmpty() (bool, error) { - volumes, err := ExtractSnapshots(r) - return len(volumes) == 0, err -} - -// ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. -func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { - var s struct { - Snapshots []Snapshot `json:"snapshots"` - } - err := (r.(SnapshotPage)).ExtractInto(&s) - return s.Snapshots, err -} - -// UpdateMetadataResult contains the response body and error from an UpdateMetadata request. -type UpdateMetadataResult struct { - commonResult -} - -// ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. -func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { - return metadata.Extract(r.BodyReader()) -} - -type commonResult struct { - golangsdk.Result -} - -// Extract will get the Snapshot object out of the commonResult object. -func (r commonResult) Extract() (*Snapshot, error) { - var s struct { - Snapshot *Snapshot `json:"snapshot"` - } - err := r.ExtractInto(&s) - return s.Snapshot, err -} diff --git a/openstack/blockstorage/v2/snapshots/testing/doc.go b/openstack/blockstorage/v2/snapshots/testing/doc.go deleted file mode 100644 index 9702a25fb..000000000 --- a/openstack/blockstorage/v2/snapshots/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// snapshots_v2 -package testing diff --git a/openstack/blockstorage/v2/snapshots/testing/fixtures.go b/openstack/blockstorage/v2/snapshots/testing/fixtures.go deleted file mode 100644 index 58789ba74..000000000 --- a/openstack/blockstorage/v2/snapshots/testing/fixtures.go +++ /dev/null @@ -1,134 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - fake "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots", 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.Fprint(w, ` - { - "snapshots": [ - { - "id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "name": "snapshot-001", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "description": "Daily Backup", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - }, - { - "id": "96c3bda7-c82a-4f50-be73-ca7621794835", - "name": "snapshot-002", - "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", - "description": "Weekly Backup", - "status": "available", - "size": 25, - "created_at": "2017-05-30T03:35:03.000000" - } - ] - } - `) - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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.Fprint(w, ` -{ - "snapshot": { - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "name": "snapshot-001", - "description": "Daily backup", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - } -} - `) - }) -} - -func MockCreateResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots", 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, ` -{ - "snapshot": { - "volume_id": "1234", - "name": "snapshot-001" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - _, _ = fmt.Fprint(w, ` -{ - "snapshot": { - "volume_id": "1234", - "name": "snapshot-001", - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "description": "Daily backup", - "volume_id": "1234", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - } -} - `) - }) -} - -func MockUpdateMetadataResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots/123/metadata", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestHeader(t, r, "Content-Type", "application/json") - th.TestJSONRequest(t, r, ` - { - "metadata": { - "key": "v1" - } - } - `) - - _, _ = fmt.Fprint(w, ` - { - "metadata": { - "key": "v1" - } - } - `) - }) -} - -func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusNoContent) - }) -} diff --git a/openstack/blockstorage/v2/snapshots/testing/requests_test.go b/openstack/blockstorage/v2/snapshots/testing/requests_test.go deleted file mode 100644 index eb114a94f..000000000 --- a/openstack/blockstorage/v2/snapshots/testing/requests_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package testing - -import ( - "testing" - "time" - - "github.com/opentelekomcloud/gophertelekomcloud/openstack/blockstorage/v2/snapshots" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - count := 0 - - _ = snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := snapshots.ExtractSnapshots(page) - if err != nil { - t.Errorf("Failed to extract snapshots: %v", err) - return false, err - } - - expected := []snapshots.Snapshot{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "snapshot-001", - VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - Status: "available", - Size: 30, - CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), - Description: "Daily Backup", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "snapshot-002", - VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", - Status: "available", - Size: 25, - CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), - Description: "Weekly Backup", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, v.Name, "snapshot-001") - th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} - n, err := snapshots.Create(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.VolumeID, "1234") - th.AssertEquals(t, n.Name, "snapshot-001") - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestUpdateMetadata(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateMetadataResponse(t) - - expected := map[string]interface{}{"key": "v1"} - - options := &snapshots.UpdateMetadataOpts{ - Metadata: map[string]interface{}{ - "key": "v1", - }, - } - - actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() - - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, actual, expected) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} diff --git a/openstack/blockstorage/v2/snapshots/urls.go b/openstack/blockstorage/v2/snapshots/urls.go deleted file mode 100644 index 337009274..000000000 --- a/openstack/blockstorage/v2/snapshots/urls.go +++ /dev/null @@ -1,27 +0,0 @@ -package snapshots - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("snapshots") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("snapshots", id) -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) -} - -func listURL(c *golangsdk.ServiceClient) string { - return createURL(c) -} - -func metadataURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("snapshots", id, "metadata") -} - -func updateMetadataURL(c *golangsdk.ServiceClient, id string) string { - return metadataURL(c, id) -} diff --git a/openstack/blockstorage/v2/snapshots/util.go b/openstack/blockstorage/v2/snapshots/util.go deleted file mode 100644 index 2375926d2..000000000 --- a/openstack/blockstorage/v2/snapshots/util.go +++ /dev/null @@ -1,22 +0,0 @@ -package snapshots - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { - return golangsdk.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} diff --git a/openstack/blockstorage/v3/snapshots/doc.go b/openstack/blockstorage/v3/snapshots/doc.go deleted file mode 100644 index 198f83077..000000000 --- a/openstack/blockstorage/v3/snapshots/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package snapshots provides information and interaction with snapshots in the -// OpenStack Block Storage service. A snapshot is a point in time copy of the -// data contained in an external storage volume, and can be controlled -// programmatically. -package snapshots diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go deleted file mode 100644 index 8b8b35c58..000000000 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ /dev/null @@ -1,189 +0,0 @@ -package snapshots - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToSnapshotCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Snapshot. This object is passed to -// the snapshots.Create function. For more information about these parameters, -// see the Snapshot object. -type CreateOpts struct { - VolumeID string `json:"volume_id" required:"true"` - Force bool `json:"force,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// ToSnapshotCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "snapshot") -} - -// Create will create a new Snapshot based on the values in CreateOpts. To -// extract the Snapshot object from the response, call the Extract method on the -// CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToSnapshotCreateMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - return -} - -// Delete will delete the existing Snapshot with the provided ID. -func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) - return -} - -// Get retrieves the Snapshot with the provided ID. To extract the Snapshot -// object from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToSnapshotListQuery() (string, error) -} - -type ListOpts struct { - // AllTenants will retrieve snapshots of all tenants/projects. - AllTenants bool `q:"all_tenants"` - - // Name will filter by the specified snapshot name. - Name string `q:"name"` - - // Status will filter by the specified status. - Status string `q:"status"` - - // TenantID will filter by a specific tenant/project ID. - // Setting AllTenants is required to use this. - TenantID string `q:"project_id"` - - // VolumeID will filter by a specified volume ID. - VolumeID string `q:"volume_id"` - - // Comma-separated list of sort keys and optional sort directions in the - // form of [:]. - Sort string `q:"sort"` - - // Requests a page size of items. - Limit int `q:"limit"` - - // Used in conjunction with limit to return a slice of items. - Offset int `q:"offset"` - - // The ID of the last-seen item. - Marker string `q:"marker"` -} - -// ToSnapshotListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToSnapshotListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - if err != nil { - return "", err - } - return q.String(), err -} - -// List returns Snapshots optionally limited by the conditions provided in -// ListOpts. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToSnapshotListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return SnapshotPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// UpdateMetadataOptsBuilder allows extensions to add additional parameters to -// the Update request. -type UpdateMetadataOptsBuilder interface { - ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) -} - -// UpdateMetadataOpts contain options for updating an existing Snapshot. This -// object is passed to the snapshots.Update function. For more information -// about the parameters, see the Snapshot object. -type UpdateMetadataOpts struct { - Metadata map[string]interface{} `json:"metadata,omitempty"` -} - -// ToSnapshotUpdateMetadataMap assembles a request body based on the contents of -// an UpdateMetadataOpts. -func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "") -} - -// UpdateMetadata will update the Snapshot with provided information. To -// extract the updated Snapshot from the response, call the ExtractMetadata -// method on the UpdateMetadataResult. -func UpdateMetadata(client *golangsdk.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { - b, err := opts.ToSnapshotUpdateMetadataMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - return -} - -// IDFromName is a convienience function that returns a snapshot's ID given its name. -func IDFromName(client *golangsdk.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractSnapshots(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", golangsdk.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} - case 1: - return id, nil - default: - return "", golangsdk.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"} - } -} diff --git a/openstack/blockstorage/v3/snapshots/results.go b/openstack/blockstorage/v3/snapshots/results.go deleted file mode 100644 index ce6061a21..000000000 --- a/openstack/blockstorage/v3/snapshots/results.go +++ /dev/null @@ -1,129 +0,0 @@ -package snapshots - -import ( - "encoding/json" - "time" - - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/common/metadata" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// Snapshot contains all the information associated with a Cinder Snapshot. -type Snapshot struct { - // Unique identifier. - ID string `json:"id"` - - // Date created. - CreatedAt time.Time `json:"-"` - - // Date updated. - UpdatedAt time.Time `json:"-"` - - // Display name. - Name string `json:"name"` - - // Display description. - Description string `json:"description"` - - // ID of the Volume from which this Snapshot was created. - VolumeID string `json:"volume_id"` - - // Currect status of the Snapshot. - Status string `json:"status"` - - // Size of the Snapshot, in GB. - Size int `json:"size"` - - // User-defined key-value pairs. - Metadata map[string]string `json:"metadata"` -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - golangsdk.ErrResult -} - -// SnapshotPage is a pagination.Pager that is returned from a call to the List function. -type SnapshotPage struct { - pagination.LinkedPageBase -} - -// UnmarshalJSON converts our JSON API response into our snapshot struct -func (r *Snapshot) UnmarshalJSON(b []byte) error { - type tmp Snapshot - var s struct { - tmp - CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Snapshot(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) - - return err -} - -// IsEmpty returns true if a SnapshotPage contains no Snapshots. -func (r SnapshotPage) IsEmpty() (bool, error) { - volumes, err := ExtractSnapshots(r) - return len(volumes) == 0, err -} - -func (r SnapshotPage) NextPageURL() (string, error) { - var s struct { - Links []golangsdk.Link `json:"snapshots_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return golangsdk.ExtractNextURL(s.Links) -} - -// ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. -func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { - var s struct { - Snapshots []Snapshot `json:"snapshots"` - } - err := (r.(SnapshotPage)).ExtractInto(&s) - return s.Snapshots, err -} - -// UpdateMetadataResult contains the response body and error from an UpdateMetadata request. -type UpdateMetadataResult struct { - commonResult -} - -// ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. -func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { - return metadata.Extract(r.BodyReader()) -} - -type commonResult struct { - golangsdk.Result -} - -// Extract will get the Snapshot object out of the commonResult object. -func (r commonResult) Extract() (*Snapshot, error) { - var s struct { - Snapshot *Snapshot `json:"snapshot"` - } - err := r.ExtractInto(&s) - return s.Snapshot, err -} diff --git a/openstack/blockstorage/v3/snapshots/testing/doc.go b/openstack/blockstorage/v3/snapshots/testing/doc.go deleted file mode 100644 index 3de6274b3..000000000 --- a/openstack/blockstorage/v3/snapshots/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// snapshots_v3 -package testing diff --git a/openstack/blockstorage/v3/snapshots/testing/fixtures.go b/openstack/blockstorage/v3/snapshots/testing/fixtures.go deleted file mode 100644 index 3d52adecf..000000000 --- a/openstack/blockstorage/v3/snapshots/testing/fixtures.go +++ /dev/null @@ -1,148 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - fake "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots", 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) - - _ = r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - _, _ = fmt.Fprintf(w, ` - { - "snapshots": [ - { - "id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "name": "snapshot-001", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "description": "Daily Backup", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - }, - { - "id": "96c3bda7-c82a-4f50-be73-ca7621794835", - "name": "snapshot-002", - "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", - "description": "Weekly Backup", - "status": "available", - "size": 25, - "created_at": "2017-05-30T03:35:03.000000" - } - ], - "snapshots_links": [ - { - "href": "%s/snapshots?marker=1", - "rel": "next" - }] - } - `, th.Server.URL) - case "1": - _, _ = fmt.Fprint(w, `{"snapshots": []}`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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.Fprint(w, ` -{ - "snapshot": { - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "name": "snapshot-001", - "description": "Daily backup", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - } -} - `) - }) -} - -func MockCreateResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots", 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, ` -{ - "snapshot": { - "volume_id": "1234", - "name": "snapshot-001" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - _, _ = fmt.Fprint(w, ` -{ - "snapshot": { - "volume_id": "1234", - "name": "snapshot-001", - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "description": "Daily backup", - "volume_id": "1234", - "status": "available", - "size": 30, - "created_at": "2017-05-30T03:35:03.000000" - } -} - `) - }) -} - -func MockUpdateMetadataResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots/123/metadata", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestHeader(t, r, "Content-Type", "application/json") - th.TestJSONRequest(t, r, ` - { - "metadata": { - "key": "v1" - } - } - `) - - _, _ = fmt.Fprint(w, ` - { - "metadata": { - "key": "v1" - } - } - `) - }) -} - -func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusNoContent) - }) -} diff --git a/openstack/blockstorage/v3/snapshots/testing/requests_test.go b/openstack/blockstorage/v3/snapshots/testing/requests_test.go deleted file mode 100644 index 8b9494dae..000000000 --- a/openstack/blockstorage/v3/snapshots/testing/requests_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package testing - -import ( - "testing" - "time" - - "github.com/opentelekomcloud/gophertelekomcloud/openstack/blockstorage/v3/snapshots" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - count := 0 - - _ = snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := snapshots.ExtractSnapshots(page) - if err != nil { - t.Errorf("Failed to extract snapshots: %v", err) - return false, err - } - - expected := []snapshots.Snapshot{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "snapshot-001", - VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - Status: "available", - Size: 30, - CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), - Description: "Daily Backup", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "snapshot-002", - VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", - Status: "available", - Size: 25, - CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), - Description: "Weekly Backup", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, v.Name, "snapshot-001") - th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} - n, err := snapshots.Create(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.VolumeID, "1234") - th.AssertEquals(t, n.Name, "snapshot-001") - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestUpdateMetadata(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateMetadataResponse(t) - - expected := map[string]interface{}{"key": "v1"} - - options := &snapshots.UpdateMetadataOpts{ - Metadata: map[string]interface{}{ - "key": "v1", - }, - } - - actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() - - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, actual, expected) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} diff --git a/openstack/blockstorage/v3/snapshots/urls.go b/openstack/blockstorage/v3/snapshots/urls.go deleted file mode 100644 index 337009274..000000000 --- a/openstack/blockstorage/v3/snapshots/urls.go +++ /dev/null @@ -1,27 +0,0 @@ -package snapshots - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("snapshots") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("snapshots", id) -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) -} - -func listURL(c *golangsdk.ServiceClient) string { - return createURL(c) -} - -func metadataURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("snapshots", id, "metadata") -} - -func updateMetadataURL(c *golangsdk.ServiceClient, id string) string { - return metadataURL(c, id) -} diff --git a/openstack/blockstorage/v3/snapshots/util.go b/openstack/blockstorage/v3/snapshots/util.go deleted file mode 100644 index 2375926d2..000000000 --- a/openstack/blockstorage/v3/snapshots/util.go +++ /dev/null @@ -1,22 +0,0 @@ -package snapshots - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { - return golangsdk.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} diff --git a/openstack/blockstorage/v3/volumes/doc.go b/openstack/blockstorage/v3/volumes/doc.go deleted file mode 100644 index 307b8b12d..000000000 --- a/openstack/blockstorage/v3/volumes/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package volumes provides information and interaction with volumes in the -// OpenStack Block Storage service. A volume is a detachable block storage -// device, akin to a USB hard drive. It can only be attached to one instance at -// a time. -package volumes diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go deleted file mode 100644 index de23f0ab0..000000000 --- a/openstack/blockstorage/v3/volumes/requests.go +++ /dev/null @@ -1,210 +0,0 @@ -package volumes - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToVolumeCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Volume. This object is passed to -// the volumes.Create function. For more information about these parameters, -// see the Volume object. -type CreateOpts struct { - // The size of the volume, in GB - Size int `json:"size" required:"true"` - // The availability zone - AvailabilityZone string `json:"availability_zone,omitempty"` - // ConsistencyGroupID is the ID of a consistency group - ConsistencyGroupID string `json:"consistencygroup_id,omitempty"` - // The volume description - Description string `json:"description,omitempty"` - // One or more metadata key and value pairs to associate with the volume - Metadata map[string]string `json:"metadata,omitempty"` - // The volume name - Name string `json:"name,omitempty"` - // the ID of the existing volume snapshot - SnapshotID string `json:"snapshot_id,omitempty"` - // SourceReplica is a UUID of an existing volume to replicate with - SourceReplica string `json:"source_replica,omitempty"` - // the ID of the existing volume - SourceVolID string `json:"source_volid,omitempty"` - // The ID of the image from which you want to create the volume. - // Required to create a bootable volume. - ImageID string `json:"imageRef,omitempty"` - // The associated volume type - VolumeType string `json:"volume_type,omitempty"` -} - -// ToVolumeCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "volume") -} - -// Create will create a new Volume based on the values in CreateOpts. To extract -// the Volume object from the response, call the Extract method on the -// CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToVolumeCreateMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - return -} - -// Delete will delete the existing Volume with the provided ID. -func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) - return -} - -// Get retrieves the Volume with the provided ID. To extract the Volume object -// from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToVolumeListQuery() (string, error) -} - -// ListOpts holds options for listing Volumes. It is passed to the volumes.List -// function. -type ListOpts struct { - // AllTenants will retrieve volumes of all tenants/projects. - AllTenants bool `q:"all_tenants"` - - // Metadata will filter results based on specified metadata. - Metadata map[string]string `q:"metadata"` - - // Name will filter by the specified volume name. - Name string `q:"name"` - - // Status will filter by the specified status. - Status string `q:"status"` - - // TenantID will filter by a specific tenant/project ID. - // Setting AllTenants is required for this. - TenantID string `q:"project_id"` - - // Comma-separated list of sort keys and optional sort directions in the - // form of [:]. - Sort string `q:"sort"` - - // Requests a page size of items. - Limit int `q:"limit"` - - // Used in conjunction with limit to return a slice of items. - Offset int `q:"offset"` - - // The ID of the last-seen item. - Marker string `q:"marker"` -} - -// ToVolumeListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToVolumeListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - if err != nil { - return "", err - } - return q.String(), err -} - -// List returns Volumes optionally limited by the conditions provided in ListOpts. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToVolumeListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return VolumePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToVolumeUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contain options for updating an existing Volume. This object is passed -// to the volumes.Update function. For more information about the parameters, see -// the Volume object. -type UpdateOpts struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// ToVolumeUpdateMap assembles a request body based on the contents of an -// UpdateOpts. -func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "volume") -} - -// Update will update the Volume with provided information. To extract the updated -// Volume from the response, call the Extract method on the UpdateResult. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToVolumeUpdateMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - return -} - -// IDFromName is a convienience function that returns a server's ID given its name. -func IDFromName(client *golangsdk.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractVolumes(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", golangsdk.ErrResourceNotFound{Name: name, ResourceType: "volume"} - case 1: - return id, nil - default: - return "", golangsdk.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"} - } -} diff --git a/openstack/blockstorage/v3/volumes/results.go b/openstack/blockstorage/v3/volumes/results.go deleted file mode 100644 index 000d63bd0..000000000 --- a/openstack/blockstorage/v3/volumes/results.go +++ /dev/null @@ -1,170 +0,0 @@ -package volumes - -import ( - "encoding/json" - "time" - - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// Attachment represents a Volume Attachment record -type Attachment struct { - AttachedAt time.Time `json:"-"` - AttachmentID string `json:"attachment_id"` - Device string `json:"device"` - HostName string `json:"host_name"` - ID string `json:"id"` - ServerID string `json:"server_id"` - VolumeID string `json:"volume_id"` -} - -// UnmarshalJSON is our unmarshalling helper -func (r *Attachment) UnmarshalJSON(b []byte) error { - type tmp Attachment - var s struct { - tmp - AttachedAt golangsdk.JSONRFC3339MilliNoZ `json:"attached_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Attachment(s.tmp) - - r.AttachedAt = time.Time(s.AttachedAt) - - return err -} - -// Volume contains all the information associated with an OpenStack Volume. -type Volume struct { - // Unique identifier for the volume. - ID string `json:"id"` - // Current status of the volume. - Status string `json:"status"` - // Size of the volume in GB. - Size int `json:"size"` - // AvailabilityZone is which availability zone the volume is in. - AvailabilityZone string `json:"availability_zone"` - // The date when this volume was created. - CreatedAt time.Time `json:"-"` - // The date when this volume was last updated - UpdatedAt time.Time `json:"-"` - // Instances onto which the volume is attached. - Attachments []Attachment `json:"attachments"` - // Human-readable display name for the volume. - Name string `json:"name"` - // Human-readable description for the volume. - Description string `json:"description"` - // The type of volume to create, either SATA or SSD. - VolumeType string `json:"volume_type"` - // The ID of the snapshot from which the volume was created - SnapshotID string `json:"snapshot_id"` - // The ID of another block storage volume from which the current volume was created - SourceVolID string `json:"source_volid"` - // Arbitrary key-value pairs defined by the user. - Metadata map[string]string `json:"metadata"` - // UserID is the id of the user who created the volume. - UserID string `json:"user_id"` - // Indicates whether this is a bootable volume. - Bootable string `json:"bootable"` - // Encrypted denotes if the volume is encrypted. - Encrypted bool `json:"encrypted"` - // ReplicationStatus is the status of replication. - ReplicationStatus string `json:"replication_status"` - // ConsistencyGroupID is the consistency group ID. - ConsistencyGroupID string `json:"consistencygroup_id"` - // Multiattach denotes if the volume is multi-attach capable. - Multiattach bool `json:"multiattach"` -} - -// UnmarshalJSON another unmarshalling function -func (r *Volume) UnmarshalJSON(b []byte) error { - type tmp Volume - var s struct { - tmp - CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Volume(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) - - return err -} - -// VolumePage is a pagination.pager that is returned from a call to the List function. -type VolumePage struct { - pagination.LinkedPageBase -} - -// IsEmpty returns true if a ListResult contains no Volumes. -func (r VolumePage) IsEmpty() (bool, error) { - volumes, err := ExtractVolumes(r) - return len(volumes) == 0, err -} - -func (r VolumePage) NextPageURL() (string, error) { - var s struct { - Links []golangsdk.Link `json:"volumes_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return golangsdk.ExtractNextURL(s.Links) -} - -// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. -func ExtractVolumes(r pagination.Page) ([]Volume, error) { - var s []Volume - err := ExtractVolumesInto(r, &s) - return s, err -} - -type commonResult struct { - golangsdk.Result -} - -// Extract will get the Volume object out of the commonResult object. -func (r commonResult) Extract() (*Volume, error) { - var s Volume - err := r.ExtractInto(&s) - return &s, err -} - -// ExtractInto converts our response data into a volume struct -func (r commonResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "volume") -} - -// ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes -func ExtractVolumesInto(r pagination.Page, v interface{}) error { - return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes") -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// UpdateResult contains the response body and error from an Update request. -type UpdateResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - golangsdk.ErrResult -} diff --git a/openstack/blockstorage/v3/volumes/testing/doc.go b/openstack/blockstorage/v3/volumes/testing/doc.go deleted file mode 100644 index a2b24b7c1..000000000 --- a/openstack/blockstorage/v3/volumes/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// volumes_v3 -package testing diff --git a/openstack/blockstorage/v3/volumes/testing/fixtures.go b/openstack/blockstorage/v3/volumes/testing/fixtures.go deleted file mode 100644 index 2f705c550..000000000 --- a/openstack/blockstorage/v3/volumes/testing/fixtures.go +++ /dev/null @@ -1,217 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - fake "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/detail", 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) - - _ = r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - _, _ = fmt.Fprintf(w, ` - { - "volumes": [ - { - "volume_type": "lvmdriver-1", - "created_at": "2015-09-17T03:35:03.000000", - "bootable": "false", - "name": "vol-001", - "os-vol-mig-status-attr:name_id": null, - "consistencygroup_id": null, - "source_volid": null, - "os-volume-replication:driver_data": null, - "multiattach": false, - "snapshot_id": null, - "replication_status": "disabled", - "os-volume-replication:extended_status": null, - "encrypted": false, - "os-vol-host-attr:host": null, - "availability_zone": "nova", - "attachments": [{ - "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", - "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", - "attached_at": "2016-08-06T14:48:20.000000", - "host_name": "foobar", - "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - "device": "/dev/vdc", - "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" - }], - "id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "size": 75, - "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", - "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", - "os-vol-mig-status-attr:migstat": null, - "metadata": {"foo": "bar"}, - "status": "available", - "description": null - }, - { - "volume_type": "lvmdriver-1", - "created_at": "2015-09-17T03:32:29.000000", - "bootable": "false", - "name": "vol-002", - "os-vol-mig-status-attr:name_id": null, - "consistencygroup_id": null, - "source_volid": null, - "os-volume-replication:driver_data": null, - "multiattach": false, - "snapshot_id": null, - "replication_status": "disabled", - "os-volume-replication:extended_status": null, - "encrypted": false, - "os-vol-host-attr:host": null, - "availability_zone": "nova", - "attachments": [], - "id": "96c3bda7-c82a-4f50-be73-ca7621794835", - "size": 75, - "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", - "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", - "os-vol-mig-status-attr:migstat": null, - "metadata": {}, - "status": "available", - "description": null - } - ], - "volumes_links": [ - { - "href": "%s/volumes/detail?marker=1", - "rel": "next" - }] -} - `, th.Server.URL) - case "1": - _, _ = fmt.Fprint(w, `{"volumes": []}`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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.Fprint(w, ` -{ - "volume": { - "volume_type": "lvmdriver-1", - "created_at": "2015-09-17T03:32:29.000000", - "bootable": "false", - "name": "vol-001", - "os-vol-mig-status-attr:name_id": null, - "consistencygroup_id": null, - "source_volid": null, - "os-volume-replication:driver_data": null, - "multiattach": false, - "snapshot_id": null, - "replication_status": "disabled", - "os-volume-replication:extended_status": null, - "encrypted": false, - "os-vol-host-attr:host": null, - "availability_zone": "nova", - "attachments": [{ - "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", - "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", - "attached_at": "2016-08-06T14:48:20.000000", - "host_name": "foobar", - "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - "device": "/dev/vdc", - "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" - }], - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "size": 75, - "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", - "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", - "os-vol-mig-status-attr:migstat": null, - "metadata": {}, - "status": "available", - "description": null - } -} - `) - }) -} - -func MockCreateResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes", 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, ` -{ - "volume": { - "name": "vol-001", - "size": 75 - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - _, _ = fmt.Fprint(w, ` -{ - "volume": { - "size": 75, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "metadata": {}, - "created_at": "2015-09-17T03:32:29.044216", - "encrypted": false, - "bootable": "false", - "availability_zone": "nova", - "attachments": [], - "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", - "status": "creating", - "description": null, - "volume_type": "lvmdriver-1", - "name": "vol-001", - "replication_status": "disabled", - "consistencygroup_id": null, - "source_volid": null, - "snapshot_id": null, - "multiattach": false - } -} - `) - }) -} - -func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockUpdateResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusOK) - _, _ = fmt.Fprint(w, ` -{ - "volume": { - "name": "vol-002" - } -} - `) - }) -} diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go deleted file mode 100644 index ae928cc42..000000000 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ /dev/null @@ -1,257 +0,0 @@ -package testing - -import ( - "testing" - "time" - - "github.com/opentelekomcloud/gophertelekomcloud/openstack/blockstorage/v3/volumes" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -func TestListWithExtensions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - count := 0 - - _ = volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := volumes.ExtractVolumes(page) - if err != nil { - t.Errorf("Failed to extract volumes: %v", err) - return false, err - } - - expected := []volumes.Volume{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "vol-001", - Attachments: []volumes.Attachment{{ - ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", - AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", - AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), - HostName: "foobar", - VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - Device: "/dev/vdc", - ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - }}, - AvailabilityZone: "nova", - Bootable: "false", - ConsistencyGroupID: "", - CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), - Description: "", - Encrypted: false, - Metadata: map[string]string{"foo": "bar"}, - Multiattach: false, - // TenantID: "304dc00909ac4d0da6c62d816bcb3459", - // ReplicationDriverData: "", - // ReplicationExtendedStatus: "", - ReplicationStatus: "disabled", - Size: 75, - SnapshotID: "", - SourceVolID: "", - Status: "available", - UserID: "ff1ce52c03ab433aaba9108c2e3ef541", - VolumeType: "lvmdriver-1", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "vol-002", - Attachments: []volumes.Attachment{}, - AvailabilityZone: "nova", - Bootable: "false", - ConsistencyGroupID: "", - CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), - Description: "", - Encrypted: false, - Metadata: map[string]string{}, - Multiattach: false, - // TenantID: "304dc00909ac4d0da6c62d816bcb3459", - // ReplicationDriverData: "", - // ReplicationExtendedStatus: "", - ReplicationStatus: "disabled", - Size: 75, - SnapshotID: "", - SourceVolID: "", - Status: "available", - UserID: "ff1ce52c03ab433aaba9108c2e3ef541", - VolumeType: "lvmdriver-1", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestListAllWithExtensions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - type VolumeWithExt struct { - volumes.Volume - extensions.VolumeTenantExt - } - - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() - th.AssertNoErr(t, err) - - var actual []VolumeWithExt - err = volumes.ExtractVolumesInto(allPages, &actual) - th.AssertNoErr(t, err) - th.AssertEquals(t, 2, len(actual)) - th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", actual[0].TenantID) -} - -func TestListAll(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() - th.AssertNoErr(t, err) - actual, err := volumes.ExtractVolumes(allPages) - th.AssertNoErr(t, err) - - expected := []volumes.Volume{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "vol-001", - Attachments: []volumes.Attachment{{ - ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", - AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", - AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), - HostName: "foobar", - VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - Device: "/dev/vdc", - ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", - }}, - AvailabilityZone: "nova", - Bootable: "false", - ConsistencyGroupID: "", - CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), - Description: "", - Encrypted: false, - Metadata: map[string]string{"foo": "bar"}, - Multiattach: false, - // TenantID: "304dc00909ac4d0da6c62d816bcb3459", - // ReplicationDriverData: "", - // ReplicationExtendedStatus: "", - ReplicationStatus: "disabled", - Size: 75, - SnapshotID: "", - SourceVolID: "", - Status: "available", - UserID: "ff1ce52c03ab433aaba9108c2e3ef541", - VolumeType: "lvmdriver-1", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "vol-002", - Attachments: []volumes.Attachment{}, - AvailabilityZone: "nova", - Bootable: "false", - ConsistencyGroupID: "", - CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), - Description: "", - Encrypted: false, - Metadata: map[string]string{}, - Multiattach: false, - // TenantID: "304dc00909ac4d0da6c62d816bcb3459", - // ReplicationDriverData: "", - // ReplicationExtendedStatus: "", - ReplicationStatus: "disabled", - Size: 75, - SnapshotID: "", - SourceVolID: "", - Status: "available", - UserID: "ff1ce52c03ab433aaba9108c2e3ef541", - VolumeType: "lvmdriver-1", - }, - } - - th.CheckDeepEquals(t, expected, actual) - -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - v, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, v.Name, "vol-001") - th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - options := &volumes.CreateOpts{Size: 75, Name: "vol-001"} - n, err := volumes.Create(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.Size, 75) - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateResponse(t) - - options := volumes.UpdateOpts{Name: "vol-002"} - v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() - th.AssertNoErr(t, err) - th.CheckEquals(t, "vol-002", v.Name) -} - -func TestGetWithExtensions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - var s struct { - volumes.Volume - extensions.VolumeTenantExt - } - err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) - th.AssertNoErr(t, err) - th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", s.TenantID) - - err = volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) - if err == nil { - t.Errorf("Expected error when providing non-pointer struct") - } -} diff --git a/openstack/blockstorage/v3/volumes/urls.go b/openstack/blockstorage/v3/volumes/urls.go deleted file mode 100644 index f145a5be2..000000000 --- a/openstack/blockstorage/v3/volumes/urls.go +++ /dev/null @@ -1,23 +0,0 @@ -package volumes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("volumes") -} - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("volumes", "detail") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("volumes", id) -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) -} - -func updateURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) -} diff --git a/openstack/blockstorage/v3/volumes/util.go b/openstack/blockstorage/v3/volumes/util.go deleted file mode 100644 index 49c848a62..000000000 --- a/openstack/blockstorage/v3/volumes/util.go +++ /dev/null @@ -1,22 +0,0 @@ -package volumes - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { - return golangsdk.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go deleted file mode 100644 index 8b2769cb7..000000000 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Package volumetypes provides information and interaction with volume types in the -OpenStack Block Storage service. A volume type is a collection of specs used to -define the volume capabilities. - -Example to list Volume Types - - allPages, err := volumetypes.List(client, volumetypes.ListOpts{}).AllPages() - if err != nil{ - panic(err) - } - volumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) - if err != nil{ - panic(err) - } - for _,vt := range volumeTypes{ - fmt.Println(vt) - } - -Example to show a Volume Type - - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - volumeType, err := volumetypes.Get(client, typeID).Extract() - if err != nil{ - panic(err) - } - fmt.Println(volumeType) - -Example to create a Volume Type - - volumeType, err := volumetypes.Create(client, volumetypes.CreateOpts{ - Name:"volume_type_001", - IsPublic:true, - Description:"description_001", - }).Extract() - if err != nil{ - panic(err) - } - fmt.Println(volumeType) - -Example to delete a Volume Type - - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - err := volumetypes.Delete(client, typeID).ExtractErr() - if err != nil{ - panic(err) - } - -Example to update a Volume Type - - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - volumetype, err = volumetypes.Update(client, typeID, volumetypes.UpdateOpts{ - Name: "volume_type_002", - Description:"description_002", - IsPublic:false, - }).Extract() - if err != nil{ - panic(err) - } - fmt.Println(volumetype) -*/ - -package volumetypes diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go deleted file mode 100644 index 822db22f8..000000000 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ /dev/null @@ -1,141 +0,0 @@ -package volumetypes - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToVolumeTypeCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Volume Type. This object is passed to -// the volumetypes.Create function. For more information about these parameters, -// see the Volume Type object. -type CreateOpts struct { - // The name of the volume type - Name string `json:"name" required:"true"` - // The volume type description - Description string `json:"description,omitempty"` - // the ID of the existing volume snapshot - IsPublic *bool `json:"os-volume-type-access:is_public,omitempty"` - // Extra spec key-value pairs defined by the user. - ExtraSpecs map[string]string `json:"extra_specs"` -} - -// ToVolumeTypeCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "volume_type") -} - -// Create will create a new Volume Type based on the values in CreateOpts. To extract -// the Volume Type object from the response, call the Extract method on the -// CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToVolumeTypeCreateMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - return -} - -// Delete will delete the existing Volume Type with the provided ID. -func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) - return -} - -// Get retrieves the Volume Type with the provided ID. To extract the Volume Type object -// from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToVolumeTypeListQuery() (string, error) -} - -// ListOpts holds options for listing Volume Types. It is passed to the volumetypes.List -// function. -type ListOpts struct { - // Comma-separated list of sort keys and optional sort directions in the - // form of [:]. - Sort string `q:"sort"` - // Requests a page size of items. - Limit int `q:"limit"` - // Used in conjunction with limit to return a slice of items. - Offset int `q:"offset"` - // The ID of the last-seen item. - Marker string `q:"marker"` -} - -// ToVolumeTypeListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToVolumeTypeListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - if err != nil { - return "", err - } - return q.String(), err -} - -// List returns Volume types. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - - if opts != nil { - query, err := opts.ToVolumeTypeListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return VolumeTypePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToVolumeTypeUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contain options for updating an existing Volume Type. This object is passed -// to the volumetypes.Update function. For more information about the parameters, see -// the Volume Type object. -type UpdateOpts struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - IsPublic *bool `json:"is_public,omitempty"` -} - -// ToVolumeUpdateMap assembles a request body based on the contents of an -// UpdateOpts. -func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "volume_type") -} - -// Update will update the Volume Type with provided information. To extract the updated -// Volume Type from the response, call the Extract method on the UpdateResult. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToVolumeTypeUpdateMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - return -} diff --git a/openstack/blockstorage/v3/volumetypes/results.go b/openstack/blockstorage/v3/volumetypes/results.go deleted file mode 100644 index a727c1ed1..000000000 --- a/openstack/blockstorage/v3/volumetypes/results.go +++ /dev/null @@ -1,94 +0,0 @@ -package volumetypes - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// Volume Type contains all the information associated with an OpenStack Volume Type. -type VolumeType struct { - // Unique identifier for the volume type. - ID string `json:"id"` - // Human-readable display name for the volume type. - Name string `json:"name"` - // Human-readable description for the volume type. - Description string `json:"description"` - // Arbitrary key-value pairs defined by the user. - ExtraSpecs map[string]string `json:"extra_specs"` - // Whether the volume type is publicly visible. - IsPublic bool `json:"is_public"` - // Qos Spec ID - QosSpecID string `json:"qos_specs_id"` - // Volume Type access public attribute - PublicAccess bool `json:"os-volume-type-access:is_public"` -} - -// VolumeTypePage is a pagination.pager that is returned from a call to the List function. -type VolumeTypePage struct { - pagination.LinkedPageBase -} - -// IsEmpty returns true if a ListResult contains no Volume Types. -func (r VolumeTypePage) IsEmpty() (bool, error) { - volumetypes, err := ExtractVolumeTypes(r) - return len(volumetypes) == 0, err -} - -func (r VolumeTypePage) NextPageURL() (string, error) { - var s struct { - Links []golangsdk.Link `json:"volume_type_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return golangsdk.ExtractNextURL(s.Links) -} - -// ExtractVolumeTypes extracts and returns Volumes. It is used while iterating over a volumetypes.List call. -func ExtractVolumeTypes(r pagination.Page) ([]VolumeType, error) { - var s []VolumeType - err := ExtractVolumeTypesInto(r, &s) - return s, err -} - -type commonResult struct { - golangsdk.Result -} - -// Extract will get the Volume Type object out of the commonResult object. -func (r commonResult) Extract() (*VolumeType, error) { - var s VolumeType - err := r.ExtractInto(&s) - return &s, err -} - -// ExtractInto converts our response data into a volume type struct -func (r commonResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "volume_type") -} - -// ExtractVolumesInto similar to ExtractInto but operates on a `list` of volume types -func ExtractVolumeTypesInto(r pagination.Page, v interface{}) error { - return r.(VolumeTypePage).Result.ExtractIntoSlicePtr(v, "volume_types") -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - golangsdk.ErrResult -} - -// UpdateResult contains the response body and error from an Update request. -type UpdateResult struct { - commonResult -} diff --git a/openstack/blockstorage/v3/volumetypes/testing/doc.go b/openstack/blockstorage/v3/volumetypes/testing/doc.go deleted file mode 100644 index 3fd720a67..000000000 --- a/openstack/blockstorage/v3/volumetypes/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// volume_types -package testing diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go deleted file mode 100644 index fa363a510..000000000 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go +++ /dev/null @@ -1,154 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - fake "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -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) - - _ = r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - _, _ = fmt.Fprintf(w, ` -{ - "volume_types": [ - { - "name": "SSD", - "qos_specs_id": null, - "os-volume-type-access:is_public": true, - "extra_specs": { - "volume_backend_name": "lvmdriver-1" - }, - "is_public": true, - "id": "6685584b-1eac-4da6-b5c3-555430cf68ff", - "description": null - }, - { - "name": "SATA", - "qos_specs_id": null, - "os-volume-type-access:is_public": true, - "extra_specs": { - "volume_backend_name": "lvmdriver-1" - }, - "is_public": true, - "id": "8eb69a46-df97-4e41-9586-9a40a7533803", - "description": null - } - ], - "volume_type_links": [ - { - "href": "%s/types?marker=1", - "rel": "next" - } - ] -} - `, th.Server.URL) - case "1": - _, _ = fmt.Fprint(w, `{"volume_types": []}`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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.Fprint(w, ` -{ - "volume_type": { - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "name": "vol-type-001", - "os-volume-type-access:is_public": true, - "qos_specs_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "description": "volume type 001", - "is_public": true, - "extra_specs": { - "capabilities": "gpu" - } - } -} -`) - }) -} - -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, ` -{ - "volume_type": { - "name": "test_type", - "os-volume-type-access:is_public": true, - "description": "test_type_desc", - "extra_specs": { - "capabilities": "gpu" - } - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - _, _ = fmt.Fprint(w, ` -{ - "volume_type": { - "name": "test_type", - "extra_specs": {}, - "is_public": true, - "os-volume-type-access:is_public": true, - "id": "6d0ff92a-0007-4780-9ece-acfe5876966a", - "description": "test_type_desc", - "extra_specs": { - "capabilities": "gpu" - } - } -} - `) - }) -} - -func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockUpdateResponse(t *testing.T) { - th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusOK) - _, _ = fmt.Fprint(w, ` -{ - "volume_type": { - "name": "vol-type-002", - "description": "volume type 0001", - "is_public": true, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" - } -}`) - }) -} diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go deleted file mode 100644 index 74cd2030d..000000000 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/openstack/blockstorage/v3/volumetypes" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -func TestListAll(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - pages := 0 - err := volumetypes.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { - pages++ - actual, err := volumetypes.ExtractVolumeTypes(page) - if err != nil { - return false, err - } - expected := []volumetypes.VolumeType{ - { - ID: "6685584b-1eac-4da6-b5c3-555430cf68ff", - Name: "SSD", - ExtraSpecs: map[string]string{"volume_backend_name": "lvmdriver-1"}, - IsPublic: true, - Description: "", - QosSpecID: "", - PublicAccess: true, - }, { - ID: "8eb69a46-df97-4e41-9586-9a40a7533803", - Name: "SATA", - ExtraSpecs: map[string]string{"volume_backend_name": "lvmdriver-1"}, - IsPublic: true, - Description: "", - QosSpecID: "", - PublicAccess: true, - }, - } - th.CheckDeepEquals(t, expected, actual) - return true, nil - }) - th.AssertNoErr(t, err) - th.AssertEquals(t, pages, 1) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - v, err := volumetypes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, v.Name, "vol-type-001") - th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertEquals(t, v.ExtraSpecs["capabilities"], "gpu") - th.AssertEquals(t, v.QosSpecID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertEquals(t, v.PublicAccess, true) -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - var isPublic = true - - options := &volumetypes.CreateOpts{ - Name: "test_type", - IsPublic: &isPublic, - Description: "test_type_desc", - ExtraSpecs: map[string]string{"capabilities": "gpu"}, - } - - n, err := volumetypes.Create(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.Name, "test_type") - th.AssertEquals(t, n.Description, "test_type_desc") - th.AssertEquals(t, n.IsPublic, true) - th.AssertEquals(t, n.PublicAccess, true) - th.AssertEquals(t, n.ID, "6d0ff92a-0007-4780-9ece-acfe5876966a") - th.AssertEquals(t, n.ExtraSpecs["capabilities"], "gpu") -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := volumetypes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateResponse(t) - - var isPublic = true - options := volumetypes.UpdateOpts{ - Name: "vol-type-002", - IsPublic: &isPublic, - } - - v, err := volumetypes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() - th.AssertNoErr(t, err) - th.CheckEquals(t, "vol-type-002", v.Name) - th.CheckEquals(t, true, v.IsPublic) -} diff --git a/openstack/blockstorage/v3/volumetypes/urls.go b/openstack/blockstorage/v3/volumetypes/urls.go deleted file mode 100644 index bfc7d5418..000000000 --- a/openstack/blockstorage/v3/volumetypes/urls.go +++ /dev/null @@ -1,23 +0,0 @@ -package volumetypes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("types") -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("types", id) -} - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("types") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("types", id) -} - -func updateURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("types", id) -} diff --git a/openstack/blockstorage/v2/snapshots/doc.go b/openstack/evs/v2/snapshots/doc.go similarity index 100% rename from openstack/blockstorage/v2/snapshots/doc.go rename to openstack/evs/v2/snapshots/doc.go diff --git a/openstack/evs/v2/snapshots/requests.go b/openstack/evs/v2/snapshots/requests.go index cb8432094..6ada877af 100644 --- a/openstack/evs/v2/snapshots/requests.go +++ b/openstack/evs/v2/snapshots/requests.go @@ -5,14 +5,9 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToSnapshotCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Snapshot. -// This object is passed to the snapshots.Create function. +// CreateOpts contains options for creating a Snapshot. This object is passed to +// the snapshots.Create function. For more information about these parameters, +// see the Snapshot object. type CreateOpts struct { VolumeID string `json:"volume_id" required:"true"` Force bool `json:"force,omitempty"` @@ -21,61 +16,22 @@ type CreateOpts struct { Metadata map[string]string `json:"metadata,omitempty"` } -// ToSnapshotCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "snapshot") -} - // Create will create a new Snapshot based on the values in CreateOpts. To // extract the Snapshot object from the response, call the Extract method on the // CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToSnapshotCreateMap() +func Create(client *golangsdk.ServiceClient, opts CreateOpts) (r CreateResult) { + b, err := golangsdk.BuildRequestBody(opts, "snapshot") if err != nil { r.Err = err return } + _, r.Err = client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) return } -// UpdateOptsBuilder allows extensions to add additional parameters to -// the Update request. -type UpdateOptsBuilder interface { - ToSnapshotUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contain options for updating an existing Snapshot. -// This object is passed to the snapshots.Update function. -type UpdateOpts struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` -} - -// ToSnapshotUpdateMap assembles a request body based on the contents of -// an UpdateOpts. -func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "snapshot") -} - -// Update will update the Snapshot with provided information. To -// extract the updated Snapshot from the response, call the ExtractMetadata -// method on the UpdateResult. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToSnapshotUpdateMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - return -} - // Delete will delete the existing Snapshot with the provided ID. func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) @@ -98,17 +54,21 @@ type ListOptsBuilder interface { // ListOpts hold options for listing Snapshots. It is passed to the // snapshots.List function. type ListOpts struct { + // AllTenants will retrieve snapshots of all tenants/projects. + AllTenants bool `q:"all_tenants"` + // Name will filter by the specified snapshot name. Name string `q:"name"` // Status will filter by the specified status. Status string `q:"status"` + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required to use this. + TenantID string `q:"project_id"` + // VolumeID will filter by a specified volume ID. VolumeID string `q:"volume_id"` - - // ID will filter by a specific snapshot ID. - ID string `q:"id"` } // ToSnapshotListQuery formats a ListOpts into a query string. @@ -135,3 +95,73 @@ func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Page return SnapshotPage{pagination.SinglePageBase(r)} }) } + +// UpdateMetadataOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateMetadataOptsBuilder interface { + ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) +} + +// UpdateMetadataOpts contain options for updating an existing Snapshot. This +// object is passed to the snapshots.Update function. For more information +// about the parameters, see the Snapshot object. +type UpdateMetadataOpts struct { + Metadata map[string]interface{} `json:"metadata,omitempty"` +} + +// ToSnapshotUpdateMetadataMap assembles a request body based on the contents of +// an UpdateMetadataOpts. +func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { + return golangsdk.BuildRequestBody(opts, "") +} + +// UpdateMetadata will update the Snapshot with provided information. To +// extract the updated Snapshot from the response, call the ExtractMetadata +// method on the UpdateMetadataResult. +func UpdateMetadata(client *golangsdk.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { + b, err := opts.ToSnapshotUpdateMetadataMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// IDFromName is a convienience function that returns a snapshot's ID given its name. +func IDFromName(client *golangsdk.ServiceClient, name string) (string, error) { + count := 0 + id := "" + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() + if err != nil { + return "", err + } + + all, err := ExtractSnapshots(pages) + if err != nil { + return "", err + } + + for _, s := range all { + if s.Name == name { + count++ + id = s.ID + } + } + + switch count { + case 0: + return "", golangsdk.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} + case 1: + return id, nil + default: + return "", golangsdk.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"} + } +} diff --git a/openstack/evs/v2/snapshots/results.go b/openstack/evs/v2/snapshots/results.go index c79e3de24..a43198651 100644 --- a/openstack/evs/v2/snapshots/results.go +++ b/openstack/evs/v2/snapshots/results.go @@ -5,6 +5,7 @@ import ( "time" "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/common/metadata" "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) @@ -38,29 +39,6 @@ type Snapshot struct { Metadata map[string]string `json:"metadata"` } -func (r *Snapshot) UnmarshalJSON(b []byte) error { - type tmp Snapshot - var s struct { - tmp - CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Snapshot(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) - - return err -} - -type commonResult struct { - golangsdk.Result -} - // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult @@ -71,11 +49,6 @@ type GetResult struct { commonResult } -// UpdateResult contains the response body and error from an Update request. -type UpdateResult struct { - commonResult -} - // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { golangsdk.ErrResult @@ -86,6 +59,25 @@ type SnapshotPage struct { pagination.SinglePageBase } +func (r *Snapshot) UnmarshalJSON(b []byte) error { + type tmp Snapshot + var s struct { + tmp + CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Snapshot(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} + // IsEmpty returns true if a SnapshotPage contains no Snapshots. func (r SnapshotPage) IsEmpty() (bool, error) { volumes, err := ExtractSnapshots(r) @@ -101,6 +93,20 @@ func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { return s.Snapshots, err } +// UpdateMetadataResult contains the response body and error from an UpdateMetadata request. +type UpdateMetadataResult struct { + commonResult +} + +// ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. +func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { + return metadata.Extract(r.BodyReader()) +} + +type commonResult struct { + golangsdk.Result +} + // Extract will get the Snapshot object out of the commonResult object. func (r commonResult) Extract() (*Snapshot, error) { var s struct { diff --git a/openstack/evs/v2/snapshots/testing/fixtures.go b/openstack/evs/v2/snapshots/testing/fixtures.go index 80a980a4e..58789ba74 100644 --- a/openstack/evs/v2/snapshots/testing/fixtures.go +++ b/openstack/evs/v2/snapshots/testing/fixtures.go @@ -9,42 +9,43 @@ import ( fake "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" ) -func MockCreateResponse(t *testing.T) { - th.Mux.HandleFunc("/cloudsnapshots", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") 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, ` -{ - "snapshot": { - "volume_id": "1234", - "name": "snapshot-001" - } -} - `) w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) + w.WriteHeader(http.StatusOK) _, _ = fmt.Fprint(w, ` -{ - "snapshot": { - "volume_id": "1234", - "name": "snapshot-001", - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "description": "Daily backup", - "status": "available", - "size": 30, - "created_at": "2020-03-27T15:35:03.000000" + { + "snapshots": [ + { + "id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "name": "snapshot-001", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "description": "Daily Backup", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + }, + { + "id": "96c3bda7-c82a-4f50-be73-ca7621794835", + "name": "snapshot-002", + "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", + "description": "Weekly Backup", + "status": "available", + "size": 25, + "created_at": "2017-05-30T03:35:03.000000" + } + ] } -} `) }) } func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/cloudsnapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) @@ -59,81 +60,73 @@ func MockGetResponse(t *testing.T) { "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", "status": "available", "size": 30, - "created_at": "2020-03-27T15:35:03.000000" + "created_at": "2017-05-30T03:35:03.000000" } } `) }) } -func MockUpdateResponse(t *testing.T) { - th.Mux.HandleFunc("/cloudsnapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots", 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, ` { "snapshot": { - "name": "snapshot-001-update", - "description": "Weekly backup" + "volume_id": "1234", + "name": "snapshot-001" } } - `) + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) _, _ = fmt.Fprint(w, ` { "snapshot": { + "volume_id": "1234", + "name": "snapshot-001", "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "name": "snapshot-001-update", - "description": "Weekly backup", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "description": "Daily backup", + "volume_id": "1234", "status": "available", "size": 30, - "created_at": "2020-03-27T15:35:03.000000", - "updated_at": "2020-03-27T15:55:03.000000" - } + "created_at": "2017-05-30T03:35:03.000000" + } } `) }) } -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/cloudsnapshots/detail", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") +func MockUpdateMetadataResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots/123/metadata", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestJSONRequest(t, r, ` + { + "metadata": { + "key": "v1" + } + } + `) _, _ = fmt.Fprint(w, ` - { - "snapshots": [ - { - "id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "name": "snapshot-001", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "description": "Daily Backup", - "status": "available", - "size": 30, - "created_at": "2020-03-27T15:35:03.000000" - }, - { - "id": "96c3bda7-c82a-4f50-be73-ca7621794835", - "name": "snapshot-002", - "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", - "description": "Weekly Backup", - "status": "available", - "size": 25, - "created_at": "2020-03-27T15:35:03.000000" + { + "metadata": { + "key": "v1" } - ] - } + } `) }) } func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/cloudsnapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) diff --git a/openstack/evs/v2/snapshots/testing/requests_test.go b/openstack/evs/v2/snapshots/testing/requests_test.go index 946ab0c48..6e4c6305a 100644 --- a/openstack/evs/v2/snapshots/testing/requests_test.go +++ b/openstack/evs/v2/snapshots/testing/requests_test.go @@ -4,59 +4,13 @@ import ( "testing" "time" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v2/snapshots" + snapshots2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v2/snapshots" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" ) -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} - n, err := snapshots.Create(client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.VolumeID, "1234") - th.AssertEquals(t, n.Name, "snapshot-001") - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, v.Name, "snapshot-001") - th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateResponse(t) - - options := snapshots.UpdateOpts{ - Name: "snapshot-001-update", - Description: "Weekly backup", - } - - v, err := snapshots.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() - - th.AssertNoErr(t, err) - th.AssertEquals(t, v.Name, "snapshot-001-update") - th.AssertEquals(t, v.Description, "Weekly backup") - th.AssertEquals(t, v.UpdatedAt, time.Date(2020, 3, 27, 15, 55, 3, 0, time.UTC)) -} - func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -65,22 +19,22 @@ func TestList(t *testing.T) { count := 0 - _ = snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + _ = snapshots2.List(client.ServiceClient(), &snapshots2.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ - actual, err := snapshots.ExtractSnapshots(page) + actual, err := snapshots2.ExtractSnapshots(page) if err != nil { t.Errorf("Failed to extract snapshots: %v", err) return false, err } - expected := []snapshots.Snapshot{ + expected := []snapshots2.Snapshot{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "snapshot-001", VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", Status: "available", Size: 30, - CreatedAt: time.Date(2020, 3, 27, 15, 35, 3, 0, time.UTC), + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), Description: "Daily Backup", }, { @@ -89,7 +43,7 @@ func TestList(t *testing.T) { VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", Status: "available", Size: 25, - CreatedAt: time.Date(2020, 3, 27, 15, 35, 3, 0, time.UTC), + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), Description: "Weekly Backup", }, } @@ -104,12 +58,60 @@ func TestList(t *testing.T) { } } +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + v, err := snapshots2.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, v.Name, "snapshot-001") + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := snapshots2.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} + n, err := snapshots2.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.VolumeID, "1234") + th.AssertEquals(t, n.Name, "snapshot-001") + th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestUpdateMetadata(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateMetadataResponse(t) + + expected := map[string]interface{}{"key": "v1"} + + options := &snapshots2.UpdateMetadataOpts{ + Metadata: map[string]interface{}{ + "key": "v1", + }, + } + + actual, err := snapshots2.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, actual, expected) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) - res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := snapshots2.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } diff --git a/openstack/evs/v2/snapshots/urls.go b/openstack/evs/v2/snapshots/urls.go index 95b374172..337009274 100644 --- a/openstack/evs/v2/snapshots/urls.go +++ b/openstack/evs/v2/snapshots/urls.go @@ -3,21 +3,25 @@ package snapshots import "github.com/opentelekomcloud/gophertelekomcloud" func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("cloudsnapshots") -} - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("cloudsnapshots/detail") + return c.ServiceURL("snapshots") } func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("cloudsnapshots", id) + return c.ServiceURL("snapshots", id) } -func updateURL(c *golangsdk.ServiceClient, id string) string { +func getURL(c *golangsdk.ServiceClient, id string) string { return deleteURL(c, id) } -func getURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) +func listURL(c *golangsdk.ServiceClient) string { + return createURL(c) +} + +func metadataURL(c *golangsdk.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id, "metadata") +} + +func updateMetadataURL(c *golangsdk.ServiceClient, id string) string { + return metadataURL(c, id) } diff --git a/openstack/blockstorage/v2/volumes/doc.go b/openstack/evs/v2/volumes/doc.go similarity index 100% rename from openstack/blockstorage/v2/volumes/doc.go rename to openstack/evs/v2/volumes/doc.go diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/evs/v2/volumes/requests.go similarity index 100% rename from openstack/blockstorage/v2/volumes/requests.go rename to openstack/evs/v2/volumes/requests.go diff --git a/openstack/blockstorage/v2/volumes/results.go b/openstack/evs/v2/volumes/results.go similarity index 100% rename from openstack/blockstorage/v2/volumes/results.go rename to openstack/evs/v2/volumes/results.go diff --git a/openstack/blockstorage/v2/volumes/testing/doc.go b/openstack/evs/v2/volumes/testing/doc.go similarity index 100% rename from openstack/blockstorage/v2/volumes/testing/doc.go rename to openstack/evs/v2/volumes/testing/doc.go diff --git a/openstack/blockstorage/v2/volumes/testing/fixtures.go b/openstack/evs/v2/volumes/testing/fixtures.go similarity index 100% rename from openstack/blockstorage/v2/volumes/testing/fixtures.go rename to openstack/evs/v2/volumes/testing/fixtures.go diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/evs/v2/volumes/testing/requests_test.go similarity index 100% rename from openstack/blockstorage/v2/volumes/testing/requests_test.go rename to openstack/evs/v2/volumes/testing/requests_test.go diff --git a/openstack/blockstorage/v2/volumes/urls.go b/openstack/evs/v2/volumes/urls.go similarity index 100% rename from openstack/blockstorage/v2/volumes/urls.go rename to openstack/evs/v2/volumes/urls.go diff --git a/openstack/blockstorage/v2/volumes/util.go b/openstack/evs/v2/volumes/util.go similarity index 100% rename from openstack/blockstorage/v2/volumes/util.go rename to openstack/evs/v2/volumes/util.go From 4d7b0c37c282df5bac0f357ca01b9e848e3b1d0c Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 13:54:35 +0100 Subject: [PATCH 04/51] Create --- openstack/evs/v3/volumes/Create.go | 89 ++++++++++++++++++++++++++++ openstack/evs/v3/volumes/requests.go | 62 ------------------- 2 files changed, 89 insertions(+), 62 deletions(-) create mode 100644 openstack/evs/v3/volumes/Create.go diff --git a/openstack/evs/v3/volumes/Create.go b/openstack/evs/v3/volumes/Create.go new file mode 100644 index 000000000..a5f93bf33 --- /dev/null +++ b/openstack/evs/v3/volumes/Create.go @@ -0,0 +1,89 @@ +package volumes + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +// CreateOpts contains options for creating a Volume. This object is passed to +// the volumes.Create function. For more information about these parameters, see the Volume object. +type CreateOpts struct { + // Specifies the disk size, in GB. Its value can be as follows: + // System disk: 1 GB to 1024 GB + // Data disk: 10 GB to 32768 GB + // + // This parameter is mandatory when you create an empty disk. + // You can specify the parameter value as required within the value range. + // + // This parameter is mandatory when you create the disk from a snapshot. + // Ensure that the disk size is greater than or equal to the snapshot size. + // + // This parameter is mandatory when you create the disk from an image. + // Ensure that the disk size is greater than or equal to the minimum disk capacity required by min_disk in the image attributes. + Size int `json:"size,omitempty"` + // Specifies the AZ where you want to create the disk. If the AZ does not exist, the disk will fail to create. + AvailabilityZone string `json:"availability_zone"` + // ConsistencyGroupID is the ID of a consistency group + // Currently, this function is not supported. + ConsistencyGroupID string `json:"consistencygroup_id,omitempty"` + // Specifies the disk description. The value can contain a maximum of 255 bytes. + Description string `json:"description,omitempty"` + // Specifies the disk metadata. The length of the key or value in the metadata cannot exceed 255 bytes. + // For details about metadata, see Parameters in the metadata field. The table lists some fields. + // You can also specify other fields based on the disk creation requirements. + // NOTE + // Parameter values under metadata cannot be null. + Metadata map[string]string `json:"metadata,omitempty"` + // Specifies the disk name. The value can contain a maximum of 255 bytes. + Name string `json:"name,omitempty"` + // Specifies the snapshot ID. If this parameter is specified, the disk is created from a snapshot. + SnapshotID string `json:"snapshot_id,omitempty"` + // Specifies the source disk ID. If this parameter is specified, the disk is cloned from an existing disk. + // Currently, this function is not supported. + SourceReplica string `json:"source_replica,omitempty"` + // Specifies the source disk ID. If this parameter is specified, the disk is cloned from an existing disk. + // Currently, this function is not supported. + SourceVolID string `json:"source_volid,omitempty"` + // Specifies the image ID. If this parameter is specified, the disk is created from an image. + // NOTE BMS system disks cannot be created from BMS images. + ImageID string `json:"imageRef,omitempty"` + // Specifies the backup ID, from which you want to create the volume. + // Create a volume from a backup is supported since 3.47 microversion + BackupID string `json:"backup_id,omitempty"` + // Specifies the disk type. + // Currently, the value can be SSD, SAS, SATA, co-p1, uh-l1, GPSSD, or ESSD. + // SSD: specifies the ultra-high I/O disk type. + // SAS: specifies the high I/O disk type. + // SATA: specifies the common I/O disk type. + // co-p1: specifies the high I/O (performance-optimized I) disk type. + // uh-l1: specifies the ultra-high I/O (latency-optimized) disk type. + // GPSSD: specifies the general purpose SSD disk type. + // ESSD: specifies the extreme SSD disk type. + // Disks of the co-p1 and uh-l1 types are used exclusively for HPC ECSs and SAP HANA ECSs. + // If the specified disk type is not available in the AZ, the disk will fail to create. + // NOTE + // If the disk is created from a snapshot, the volume_type field must be the same as that of the snapshot's source disk. + VolumeType string `json:"volume_type,omitempty"` + // Specifies whether the disk is shareable. The default value is false. + // true: specifies a shared disk. + // false: specifies a non-shared disk. + Multiattach bool `json:"multiattach,omitempty"` +} + +// Create will create a new Volume based on the values in CreateOpts. +func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Volume, error) { + b, err := build.RequestBody(opts, "volume") + if err != nil { + return nil, err + } + + raw, err := client.Post(createURL(client), b, nil, nil) + if err != nil { + return nil, err + } + + var res Volume + err = extract.IntoStructPtr(raw.Body, &res, "volume") + return &res, err +} diff --git a/openstack/evs/v3/volumes/requests.go b/openstack/evs/v3/volumes/requests.go index 2540fee01..69ecc542b 100644 --- a/openstack/evs/v3/volumes/requests.go +++ b/openstack/evs/v3/volumes/requests.go @@ -5,68 +5,6 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToVolumeCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Volume. This object is passed to -// the volumes.Create function. For more information about these parameters, -// see the Volume object. -type CreateOpts struct { - // The size of the volume, in GB - Size int `json:"size,omitempty"` - // The availability zone - AvailabilityZone string `json:"availability_zone,omitempty"` - // ConsistencyGroupID is the ID of a consistency group - ConsistencyGroupID string `json:"consistencygroup_id,omitempty"` - // The volume description - Description string `json:"description,omitempty"` - // One or more metadata key and value pairs to associate with the volume - Metadata map[string]string `json:"metadata,omitempty"` - // The volume name - Name string `json:"name,omitempty"` - // the ID of the existing volume snapshot - SnapshotID string `json:"snapshot_id,omitempty"` - // SourceReplica is a UUID of an existing volume to replicate with - SourceReplica string `json:"source_replica,omitempty"` - // the ID of the existing volume - SourceVolID string `json:"source_volid,omitempty"` - // The ID of the image from which you want to create the volume. - // Required to create a bootable volume. - ImageID string `json:"imageRef,omitempty"` - // Specifies the backup ID, from which you want to create the volume. - // Create a volume from a backup is supported since 3.47 microversion - BackupID string `json:"backup_id,omitempty"` - // The associated volume type - VolumeType string `json:"volume_type,omitempty"` - // Multiattach denotes if the volume is multi-attach capable. - Multiattach bool `json:"multiattach,omitempty"` -} - -// ToVolumeCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "volume") -} - -// Create will create a new Volume based on the values in CreateOpts. To extract -// the Volume object from the response, call the Extract method on the -// CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToVolumeCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - // DeleteOptsBuilder allows extensions to add additional parameters to the // Delete request. type DeleteOptsBuilder interface { From bc69d511903deb7c1853ea7df7f0bcef6cbb0659 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 15:46:17 +0100 Subject: [PATCH 05/51] Create --- openstack/evs/v3/volumes/Create.go | 27 +++++++++++++++++++ openstack/evs/v3/volumes/results.go | 42 ++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/openstack/evs/v3/volumes/Create.go b/openstack/evs/v3/volumes/Create.go index a5f93bf33..255e9490e 100644 --- a/openstack/evs/v3/volumes/Create.go +++ b/openstack/evs/v3/volumes/Create.go @@ -71,6 +71,33 @@ type CreateOpts struct { Multiattach bool `json:"multiattach,omitempty"` } +// Metadata +// The preceding table provides only some parameters in metadata for your reference. You can also specify other fields based on the disk creation requirements. +// If the disk is created from a snapshot, __system__encrypted and __system__cmkid are not supported, and the newly created disk has the same encryption attribute as that of the snapshot's source disk. +// If the disk is created from an image, __system__encrypted and __system__cmkid are not supported, and the newly created disk has the same encryption attribute as that of the image. +// If the disk is created from a snapshot, hw:passthrough is not supported, and the newly created disk has the same device type as that of the snapshot's source disk. +// If the disk is created from an image, hw:passthrough is not supported, and the device type of newly created disk is VBD. +type Metadata struct { + // Specifies the encryption field in metadata. The value can be 0 (not encrypted) or 1 (encrypted). + // If this parameter does not exist, the disk will not be encrypted by default. + SystemEncrypted string `json:"__system__encrypted"` + // Specifies the encryption CMK ID in metadata. This parameter is used together with + // __system__encrypted for encryption. The length of cmkid is fixed at 36 bytes. + // NOTE + // For details about how to obtain the CMK ID, see Querying the List of CMKs in the Key Management Service API Reference. + SystemCmkId string `json:"__system__cmkid"` + // If this parameter is set to true, the disk device type is SCSI, that is, + // Small Computer System Interface (SCSI), which allows ECS OSs to directly + // access the underlying storage media and supports SCSI reservation commands. + // If this parameter is set to false, the disk device type will be VBD, which supports only simple SCSI read/write commands. + // If this parameter does not appear, the disk device type is VBD. + // NOTE + // If parameter shareable is set to true and parameter hw:passthrough is not specified, shared VBD disks are created. + Passthrough string `json:"hw:passthrough"` + // If the disk is created from a snapshot and linked cloning needs to be used, set this parameter to 0. + FullClone string `json:"full_clone"` +} + // Create will create a new Volume based on the values in CreateOpts. func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Volume, error) { b, err := build.RequestBody(opts, "volume") diff --git a/openstack/evs/v3/volumes/results.go b/openstack/evs/v3/volumes/results.go index 667d2ea63..4f5d39360 100644 --- a/openstack/evs/v3/volumes/results.go +++ b/openstack/evs/v3/volumes/results.go @@ -10,13 +10,21 @@ import ( // Attachment represents a Volume Attachment record type Attachment struct { - AttachedAt time.Time `json:"-"` - AttachmentID string `json:"attachment_id"` - Device string `json:"device"` - HostName string `json:"host_name"` - ID string `json:"id"` - ServerID string `json:"server_id"` - VolumeID string `json:"volume_id"` + // Specifies the time when the disk was attached. + // Time format: UTC YYYY-MM-DDTHH:MM:SS.XXXXXX + AttachedAt time.Time `json:"-"` + // Specifies the ID of the attachment information. + AttachmentID string `json:"attachment_id"` + // Specifies the device name. + Device string `json:"device"` + // Specifies the name of the physical host accommodating the server to which the disk is attached. + HostName string `json:"host_name"` + // Specifies the ID of the attached resource. + ID string `json:"id"` + // Specifies the ID of the server to which the disk is attached. + ServerID string `json:"server_id"` + // Specifies the disk ID. + VolumeID string `json:"volume_id"` } // UnmarshalJSON is our unmarshalling helper @@ -41,6 +49,8 @@ func (r *Attachment) UnmarshalJSON(b []byte) error { type Volume struct { // Unique identifier for the volume. ID string `json:"id"` + // Specifies the disk URI. + Links []golangsdk.Link `json:"links"` // Current status of the volume. Status string `json:"status"` // Size of the volume in GB. @@ -57,15 +67,25 @@ type Volume struct { Name string `json:"name"` // Human-readable description for the volume. Description string `json:"description"` - // The type of volume to create, either SATA or SSD. + // Specifies the disk type. + // Currently, the value can be SSD, SAS, SATA, co-p1, uh-l1, GPSSD, or ESSD. + // SSD: specifies the ultra-high I/O disk type. + // SAS: specifies the high I/O disk type. + // SATA: specifies the common I/O disk type. + // co-p1: specifies the high I/O (performance-optimized I) disk type. + // uh-l1: specifies the ultra-high I/O (latency-optimized) disk type. + // GPSSD: specifies the general purpose SSD disk type. + // ESSD: specifies the extreme SSD disk type. + // Disks of the co-p1 and uh-l1 types are used exclusively for HPC ECSs and SAP HANA ECSs. VolumeType string `json:"volume_type"` // The ID of the snapshot from which the volume was created SnapshotID string `json:"snapshot_id"` // The ID of another block storage volume from which the current volume was created + // Currently, this field is not supported by EVS. SourceVolID string `json:"source_volid"` // The backup ID, from which the volume was restored // This field is supported since 3.47 microversion - BackupID *string `json:"backup_id"` + BackupID string `json:"backup_id"` // Arbitrary key-value pairs defined by the user. Metadata map[string]string `json:"metadata"` // UserID is the id of the user who created the volume. @@ -73,12 +93,14 @@ type Volume struct { // Indicates whether this is a bootable volume. Bootable string `json:"bootable"` // Encrypted denotes if the volume is encrypted. + // Currently, this field is not supported by EVS. Encrypted bool `json:"encrypted"` // ReplicationStatus is the status of replication. + // Currently, this field is not supported by EVS. ReplicationStatus string `json:"replication_status"` // ConsistencyGroupID is the consistency group ID. ConsistencyGroupID string `json:"consistencygroup_id"` - // Multiattach denotes if the volume is multi-attach capable. + // Specifies whether the disk is shareable. Multiattach bool `json:"multiattach"` // Image metadata entries, only included for volumes that were created from an image, or from a snapshot of a volume originally created from an image. VolumeImageMetadata map[string]string `json:"volume_image_metadata"` From c77043ba43d8c1a82018fb53e8f63734a7726f58 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 15:51:02 +0100 Subject: [PATCH 06/51] Delete --- openstack/evs/v3/volumes/Create.go | 3 +- openstack/evs/v3/volumes/Delete.go | 22 +++++++++++++++ openstack/evs/v3/volumes/requests.go | 41 ++-------------------------- openstack/evs/v3/volumes/urls.go | 23 ---------------- 4 files changed, 27 insertions(+), 62 deletions(-) create mode 100644 openstack/evs/v3/volumes/Delete.go delete mode 100644 openstack/evs/v3/volumes/urls.go diff --git a/openstack/evs/v3/volumes/Create.go b/openstack/evs/v3/volumes/Create.go index 255e9490e..7fe2d136d 100644 --- a/openstack/evs/v3/volumes/Create.go +++ b/openstack/evs/v3/volumes/Create.go @@ -105,7 +105,8 @@ func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Volume, error) { return nil, err } - raw, err := client.Post(createURL(client), b, nil, nil) + // POST /v3/{project_id}/volumes + raw, err := client.Post(client.ServiceURL("volumes"), b, nil, nil) if err != nil { return nil, err } diff --git a/openstack/evs/v3/volumes/Delete.go b/openstack/evs/v3/volumes/Delete.go new file mode 100644 index 000000000..0a07c322b --- /dev/null +++ b/openstack/evs/v3/volumes/Delete.go @@ -0,0 +1,22 @@ +package volumes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// DeleteOpts contains options for deleting a Volume. +type DeleteOpts struct { + VolumeId string + // Specifies to delete all snapshots associated with the disk. The default value is false. + Cascade bool `q:"cascade"` +} + +// Delete will delete the existing Volume with the provided ID. +func Delete(client *golangsdk.ServiceClient, opts DeleteOpts) (err error) { + q, err := golangsdk.BuildQueryString(opts) + if err != nil { + return + } + + // DELETE /v3/{project_id}/volumes/{volume_id} + _, err = client.Delete(client.ServiceURL("volumes", opts.VolumeId)+q.String(), nil) + return +} diff --git a/openstack/evs/v3/volumes/requests.go b/openstack/evs/v3/volumes/requests.go index 69ecc542b..0783eda2f 100644 --- a/openstack/evs/v3/volumes/requests.go +++ b/openstack/evs/v3/volumes/requests.go @@ -5,45 +5,10 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) -// DeleteOptsBuilder allows extensions to add additional parameters to the -// Delete request. -type DeleteOptsBuilder interface { - ToVolumeDeleteQuery() (string, error) -} - -// DeleteOpts contains options for deleting a Volume. This object is passed to -// the volumes.Delete function. -type DeleteOpts struct { - // Delete all snapshots of this volume as well. - Cascade bool `q:"cascade"` -} - -// ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string. -func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// Delete will delete the existing Volume with the provided ID. -func Delete(client *golangsdk.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { - url := deleteURL(client, id) - if opts != nil { - query, err := opts.ToVolumeDeleteQuery() - if err != nil { - r.Err = err - return - } - url += query - } - resp, err := client.Delete(url, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(client.ServiceURL("volumes", id), &r.Body, nil) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -95,7 +60,7 @@ func (opts ListOpts) ToVolumeListQuery() (string, error) { // List returns Volumes optionally limited by the conditions provided in ListOpts. func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) + url := client.ServiceURL("volumes", "detail") if opts != nil { query, err := opts.ToVolumeListQuery() if err != nil { @@ -138,7 +103,7 @@ func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ + resp, err := client.Put(client.ServiceURL("volumes", id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) diff --git a/openstack/evs/v3/volumes/urls.go b/openstack/evs/v3/volumes/urls.go deleted file mode 100644 index f145a5be2..000000000 --- a/openstack/evs/v3/volumes/urls.go +++ /dev/null @@ -1,23 +0,0 @@ -package volumes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("volumes") -} - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("volumes", "detail") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("volumes", id) -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) -} - -func updateURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) -} From 336156587be74a66261b90db7ff489c271163cde Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 15:55:48 +0100 Subject: [PATCH 07/51] Update --- openstack/evs/v3/volumes/Get.go | 11 +++ openstack/evs/v3/volumes/List.go | 61 +++++++++++++++ openstack/evs/v3/volumes/Update.go | 40 ++++++++++ openstack/evs/v3/volumes/requests.go | 111 --------------------------- openstack/evs/v3/volumes/results.go | 17 ++++ openstack/evs/v3/volumes/util.go | 22 ------ 6 files changed, 129 insertions(+), 133 deletions(-) create mode 100644 openstack/evs/v3/volumes/Get.go create mode 100644 openstack/evs/v3/volumes/List.go create mode 100644 openstack/evs/v3/volumes/Update.go delete mode 100644 openstack/evs/v3/volumes/requests.go delete mode 100644 openstack/evs/v3/volumes/util.go diff --git a/openstack/evs/v3/volumes/Get.go b/openstack/evs/v3/volumes/Get.go new file mode 100644 index 000000000..107a818c1 --- /dev/null +++ b/openstack/evs/v3/volumes/Get.go @@ -0,0 +1,11 @@ +package volumes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// Get retrieves the Volume with the provided ID. To extract the Volume object +// from the response, call the Extract method on the GetResult. +func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(client.ServiceURL("volumes", id), &r.Body, nil) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumes/List.go b/openstack/evs/v3/volumes/List.go new file mode 100644 index 000000000..5bc0d6763 --- /dev/null +++ b/openstack/evs/v3/volumes/List.go @@ -0,0 +1,61 @@ +package volumes + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" +) + +// ListOpts holds options for listing Volumes. It is passed to the volumes.List +// function. +type ListOpts struct { + // AllTenants will retrieve volumes of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Metadata will filter results based on specified metadata. + Metadata map[string]string `q:"metadata"` + + // Name will filter by the specified volume name. + Name string `q:"name"` + + // Status will filter by the specified status. + Status string `q:"status"` + + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required for this. + TenantID string `q:"project_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToVolumeListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToVolumeListQuery() (string, error) { + q, err := golangsdk.BuildQueryString(opts) + return q.String(), err +} + +// List returns Volumes optionally limited by the conditions provided in ListOpts. +func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := client.ServiceURL("volumes", "detail") + if opts != nil { + query, err := opts.ToVolumeListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return VolumePage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/evs/v3/volumes/Update.go b/openstack/evs/v3/volumes/Update.go new file mode 100644 index 000000000..cc14c2d0e --- /dev/null +++ b/openstack/evs/v3/volumes/Update.go @@ -0,0 +1,40 @@ +package volumes + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) + +// UpdateOpts contain options for updating an existing Volume. This object is passed +// to the volumes.Update function. For more information about the parameters, see +// the Volume object. +type UpdateOpts struct { + // Specifies the disk name. The value can contain a maximum of 255 bytes. + Name string `json:"name,omitempty"` + // Specifies the disk description. The value can contain a maximum of 255 bytes. + Description string `json:"description,omitempty"` + // Specifies the disk metadata. + // The length of the key or value in the metadata cannot exceed 255 bytes. + Metadata map[string]string `json:"metadata,omitempty"` + // Specifies also the disk name. You can specify either parameter name or display_name. + // If both parameters are specified, the name value is used. The value can contain a maximum of 255 bytes. + DisplayName string `json:"display_name,omitempty"` + // Specifies also the disk description. You can specify either parameter description or display_description. + // If both parameters are specified, the description value is used. The value can contain a maximum of 255 bytes. + DisplayDescription string `json:"display_description,omitempty"` +} + +// Update will update the Volume with provided information. To extract the updated +// Volume from the response, call the Extract method on the UpdateResult. +func Update(client *golangsdk.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { + b, err := build.RequestBody(opts, "volume") + if err != nil { + r.Err = err + return + } + resp, err := client.Put(client.ServiceURL("volumes", id), b, &r.Body, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumes/requests.go b/openstack/evs/v3/volumes/requests.go deleted file mode 100644 index 0783eda2f..000000000 --- a/openstack/evs/v3/volumes/requests.go +++ /dev/null @@ -1,111 +0,0 @@ -package volumes - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// Get retrieves the Volume with the provided ID. To extract the Volume object -// from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(client.ServiceURL("volumes", id), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToVolumeListQuery() (string, error) -} - -// ListOpts holds options for listing Volumes. It is passed to the volumes.List -// function. -type ListOpts struct { - // AllTenants will retrieve volumes of all tenants/projects. - AllTenants bool `q:"all_tenants"` - - // Metadata will filter results based on specified metadata. - Metadata map[string]string `q:"metadata"` - - // Name will filter by the specified volume name. - Name string `q:"name"` - - // Status will filter by the specified status. - Status string `q:"status"` - - // TenantID will filter by a specific tenant/project ID. - // Setting AllTenants is required for this. - TenantID string `q:"project_id"` - - // Comma-separated list of sort keys and optional sort directions in the - // form of [:]. - Sort string `q:"sort"` - - // Requests a page size of items. - Limit int `q:"limit"` - - // Used in conjunction with limit to return a slice of items. - Offset int `q:"offset"` - - // The ID of the last-seen item. - Marker string `q:"marker"` -} - -// ToVolumeListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToVolumeListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// List returns Volumes optionally limited by the conditions provided in ListOpts. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := client.ServiceURL("volumes", "detail") - if opts != nil { - query, err := opts.ToVolumeListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return VolumePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToVolumeUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contain options for updating an existing Volume. This object is passed -// to the volumes.Update function. For more information about the parameters, see -// the Volume object. -type UpdateOpts struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// ToVolumeUpdateMap assembles a request body based on the contents of an -// UpdateOpts. -func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "volume") -} - -// Update will update the Volume with provided information. To extract the updated -// Volume from the response, call the Extract method on the UpdateResult. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToVolumeUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(client.ServiceURL("volumes", id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumes/results.go b/openstack/evs/v3/volumes/results.go index 4f5d39360..f4e6197bb 100644 --- a/openstack/evs/v3/volumes/results.go +++ b/openstack/evs/v3/volumes/results.go @@ -195,3 +195,20 @@ type UpdateResult struct { type DeleteResult struct { golangsdk.ErrResult } + +// WaitForStatus will continually poll the resource, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { + return golangsdk.WaitFor(secs, func() (bool, error) { + current, err := Get(c, id).Extract() + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} diff --git a/openstack/evs/v3/volumes/util.go b/openstack/evs/v3/volumes/util.go deleted file mode 100644 index 49c848a62..000000000 --- a/openstack/evs/v3/volumes/util.go +++ /dev/null @@ -1,22 +0,0 @@ -package volumes - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { - return golangsdk.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} From ac36160c2d4a6f0d7601d3dee779499e15c7eaa4 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 16:02:56 +0100 Subject: [PATCH 08/51] Update --- openstack/evs/v3/volumes/Create.go | 9 +-------- openstack/evs/v3/volumes/Update.go | 11 +++++------ openstack/evs/v3/volumes/results.go | 12 ++++++++++++ 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/openstack/evs/v3/volumes/Create.go b/openstack/evs/v3/volumes/Create.go index 7fe2d136d..57c2f59e7 100644 --- a/openstack/evs/v3/volumes/Create.go +++ b/openstack/evs/v3/volumes/Create.go @@ -3,7 +3,6 @@ package volumes import ( "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/internal/build" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" ) // CreateOpts contains options for creating a Volume. This object is passed to @@ -107,11 +106,5 @@ func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Volume, error) { // POST /v3/{project_id}/volumes raw, err := client.Post(client.ServiceURL("volumes"), b, nil, nil) - if err != nil { - return nil, err - } - - var res Volume - err = extract.IntoStructPtr(raw.Body, &res, "volume") - return &res, err + return extra(err, raw) } diff --git a/openstack/evs/v3/volumes/Update.go b/openstack/evs/v3/volumes/Update.go index cc14c2d0e..60967043b 100644 --- a/openstack/evs/v3/volumes/Update.go +++ b/openstack/evs/v3/volumes/Update.go @@ -26,15 +26,14 @@ type UpdateOpts struct { // Update will update the Volume with provided information. To extract the updated // Volume from the response, call the Extract method on the UpdateResult. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { +func Update(client *golangsdk.ServiceClient, id string, opts UpdateOpts) (*Volume, error) { b, err := build.RequestBody(opts, "volume") if err != nil { - r.Err = err - return + return nil, err } - resp, err := client.Put(client.ServiceURL("volumes", id), b, &r.Body, &golangsdk.RequestOpts{ + + raw, err := client.Put(client.ServiceURL("volumes", id), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return + return extra(err, raw) } diff --git a/openstack/evs/v3/volumes/results.go b/openstack/evs/v3/volumes/results.go index f4e6197bb..6a949c454 100644 --- a/openstack/evs/v3/volumes/results.go +++ b/openstack/evs/v3/volumes/results.go @@ -2,9 +2,11 @@ package volumes import ( "encoding/json" + "net/http" "time" "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) @@ -126,6 +128,16 @@ func (r *Volume) UnmarshalJSON(b []byte) error { return err } +func extra(err error, raw *http.Response) (*Volume, error) { + if err != nil { + return nil, err + } + + var res Volume + err = extract.IntoStructPtr(raw.Body, &res, "volume") + return &res, err +} + // VolumePage is a pagination.pager that is returned from a call to the List function. type VolumePage struct { pagination.LinkedPageBase From e8b0dbb1a892fe546f68e365949f959f69c6bd59 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 16:09:48 +0100 Subject: [PATCH 09/51] List --- openstack/evs/v3/volumes/List.go | 36 ++++++++------------------------ 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/openstack/evs/v3/volumes/List.go b/openstack/evs/v3/volumes/List.go index 5bc0d6763..25867ea4f 100644 --- a/openstack/evs/v3/volumes/List.go +++ b/openstack/evs/v3/volumes/List.go @@ -5,57 +5,39 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) -// ListOpts holds options for listing Volumes. It is passed to the volumes.List -// function. +// ListOpts holds options for listing Volumes. It is passed to the volumes.List function. type ListOpts struct { // AllTenants will retrieve volumes of all tenants/projects. AllTenants bool `q:"all_tenants"` - // Metadata will filter results based on specified metadata. Metadata map[string]string `q:"metadata"` - // Name will filter by the specified volume name. Name string `q:"name"` - // Status will filter by the specified status. Status string `q:"status"` - // TenantID will filter by a specific tenant/project ID. // Setting AllTenants is required for this. TenantID string `q:"project_id"` - // Comma-separated list of sort keys and optional sort directions in the // form of [:]. Sort string `q:"sort"` - // Requests a page size of items. Limit int `q:"limit"` - // Used in conjunction with limit to return a slice of items. Offset int `q:"offset"` - // The ID of the last-seen item. Marker string `q:"marker"` } -// ToVolumeListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToVolumeListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - // List returns Volumes optionally limited by the conditions provided in ListOpts. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := client.ServiceURL("volumes", "detail") - if opts != nil { - query, err := opts.ToVolumeListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query +func List(client *golangsdk.ServiceClient, opts ListOpts) pagination.Pager { + q, err := golangsdk.BuildQueryString(opts) + if err != nil { + return pagination.Pager{Err: err} } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return VolumePage{pagination.LinkedPageBase{PageResult: r}} - }) + return pagination.NewPager(client, client.ServiceURL("volumes", "detail")+q.String(), + func(r pagination.PageResult) pagination.Page { + return VolumePage{pagination.LinkedPageBase{PageResult: r}} + }) } From 048589a0a2b3bdc4767c1e6d8fc24d0befde7785 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 16:17:14 +0100 Subject: [PATCH 10/51] Get --- openstack/evs/v3/volumes/Get.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/openstack/evs/v3/volumes/Get.go b/openstack/evs/v3/volumes/Get.go index 107a818c1..09429c363 100644 --- a/openstack/evs/v3/volumes/Get.go +++ b/openstack/evs/v3/volumes/Get.go @@ -2,10 +2,9 @@ package volumes import "github.com/opentelekomcloud/gophertelekomcloud" -// Get retrieves the Volume with the provided ID. To extract the Volume object -// from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(client.ServiceURL("volumes", id), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return +// Get retrieves the Volume with the provided ID. +func Get(client *golangsdk.ServiceClient, id string) (*Volume, error) { + // GET /v3/{project_id}/volumes/{volume_id} + raw, err := client.Get(client.ServiceURL("volumes", id), nil, nil) + return extra(err, raw) } From 676cc54f783b038fc146e80f8aa98c74fb1dc451 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 17:19:11 +0100 Subject: [PATCH 11/51] Test --- acceptance/openstack/compute/v2/helper.go | 79 ++++++++++ .../openstack/compute/v2/servers_test.go | 82 +--------- .../openstack/evs/extensions/extensions.go | 38 ++--- .../openstack/evs/extensions/limits_test.go | 2 +- .../evs/extensions/schedulerhints_test.go | 17 ++- .../evs/extensions/volumeactions_test.go | 14 +- .../evs/extensions/volumetenants_test.go | 2 +- .../openstack/evs/noauth/blockstorage.go | 142 ------------------ .../openstack/evs/noauth/snapshots_test.go | 56 ------- .../openstack/evs/noauth/volumes_test.go | 50 ------ acceptance/openstack/evs/v3/blockstorage.go | 26 ++-- acceptance/openstack/evs/v3/qos_test.go | 14 +- acceptance/openstack/evs/v3/quotaset_test.go | 28 ++-- acceptance/openstack/evs/v3/snapshots_test.go | 2 +- .../openstack/evs/v3/volumeattachments.go | 12 +- .../evs/v3/volumeattachments_test.go | 6 +- acceptance/openstack/evs/v3/volumes_test.go | 35 ++--- .../openstack/evs/v3/volumetypes_test.go | 16 +- .../evs/extensions/schedulerhints/requests.go | 25 +-- openstack/evs/v3/volumes/Create.go | 13 +- openstack/evs/v3/volumes/List.go | 35 +++++ openstack/evs/v3/volumes/Update.go | 6 +- openstack/evs/v3/volumes/results.go | 73 +-------- 23 files changed, 245 insertions(+), 528 deletions(-) create mode 100644 acceptance/openstack/compute/v2/helper.go delete mode 100644 acceptance/openstack/evs/noauth/blockstorage.go delete mode 100644 acceptance/openstack/evs/noauth/snapshots_test.go delete mode 100644 acceptance/openstack/evs/noauth/volumes_test.go diff --git a/acceptance/openstack/compute/v2/helper.go b/acceptance/openstack/compute/v2/helper.go new file mode 100644 index 000000000..2d77070ce --- /dev/null +++ b/acceptance/openstack/compute/v2/helper.go @@ -0,0 +1,79 @@ +package v2 + +import ( + "testing" + "time" + + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/flavors" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/images" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/servers" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func CreateServer(t *testing.T, client *golangsdk.ServiceClient) (*servers.Server, error) { + t.Logf("Attempting to create ECSv2") + ecsName := tools.RandomString("create-ecs-", 3) + + az := clients.EnvOS.GetEnv("AVAILABILITY_ZONE") + if az == "" { + az = "eu-de-01" + } + + networkID := clients.EnvOS.GetEnv("NETWORK_ID") + if networkID == "" { + t.Skip("OS_NETWORK_ID env var is missing but ECS test requires using existing network") + } + + imageID, err := images.IDFromName(client, "Standard_Debian_10_latest") + th.AssertNoErr(t, err) + + flavorID, err := flavors.IDFromName(client, "s2.large.2") + th.AssertNoErr(t, err) + + ecs, err := servers.Create(client, servers.CreateOpts{ + Name: ecsName, + ImageRef: imageID, + FlavorRef: flavorID, + SecurityGroups: []string{ + openstack.DefaultSecurityGroup(t), + }, + AvailabilityZone: az, + Networks: []servers.Network{ + { + UUID: networkID, + }, + }, + }).Extract() + th.AssertNoErr(t, err) + + err = servers.WaitForStatus(client, ecs.ID, "ACTIVE", 1200) + th.AssertNoErr(t, err) + t.Logf("Created ECSv2: %s", ecs.ID) + return ecs, err +} + +func DeleteServer(t *testing.T, client *golangsdk.ServiceClient, ecs *servers.Server) { + t.Logf("Attempting to delete ECSv2: %s", ecs.ID) + + _, err := servers.Delete(client, ecs.ID).ExtractJobResponse() + th.AssertNoErr(t, err) + + err = golangsdk.WaitFor(1200, func() (bool, error) { + _, err := servers.Get(client, ecs.ID).Extract() + if err != nil { + if _, ok := err.(golangsdk.ErrDefault400); ok { + time.Sleep(10 * time.Second) + return false, nil + } + return false, err + } + return true, nil + }) + th.AssertNoErr(t, err) + + t.Logf("ECSv2 instance deleted: %s", ecs.ID) +} diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 3c04bf50b..f409e63b7 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -2,14 +2,9 @@ package v2 import ( "testing" - "time" - "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/flavors" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/images" "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/servers" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) @@ -34,85 +29,22 @@ func TestServerLifecycle(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - t.Logf("Attempting to create ECSv2") - ecsName := tools.RandomString("create-ecs-", 3) - - az := clients.EnvOS.GetEnv("AVAILABILITY_ZONE") - if az == "" { - az = "eu-de-01" - } - - networkID := clients.EnvOS.GetEnv("NETWORK_ID") - if networkID == "" { - t.Skip("OS_NETWORK_ID env var is missing but ECS test requires using existing network") - } - - imageID, err := images.IDFromName(client, "Standard_Debian_10_latest") - th.AssertNoErr(t, err) - - flavorID, err := flavors.IDFromName(client, "s2.large.2") - th.AssertNoErr(t, err) - - createOpts := servers.CreateOpts{ - Name: ecsName, - ImageRef: imageID, - FlavorRef: flavorID, - SecurityGroups: []string{ - openstack.DefaultSecurityGroup(t), - }, - AvailabilityZone: az, - Networks: []servers.Network{ - { - UUID: networkID, - }, - }, - } - - ecs, err := servers.Create(client, createOpts).Extract() + ecs, err := CreateServer(t, client) th.AssertNoErr(t, err) - - err = servers.WaitForStatus(client, ecs.ID, "ACTIVE", 1200) - th.AssertNoErr(t, err) - t.Logf("Created ECSv2: %s", ecs.ID) - - ecs, err = servers.Get(client, ecs.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ecsName, ecs.Name) + t.Cleanup(func() { + DeleteServer(t, client, ecs) + }) nicInfo, err := servers.GetNICs(client, ecs.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, nicInfo) - defer func() { - t.Logf("Attempting to delete ECSv2: %s", ecs.ID) - - _, err := servers.Delete(client, ecs.ID).ExtractJobResponse() - th.AssertNoErr(t, err) - - err = golangsdk.WaitFor(1200, func() (bool, error) { - _, err := servers.Get(client, ecs.ID).Extract() - if err != nil { - if _, ok := err.(golangsdk.ErrDefault400); ok { - time.Sleep(10 * time.Second) - return false, nil - } - return false, err - } - return true, nil - }) - th.AssertNoErr(t, err) - - t.Logf("ECSv2 instance deleted: %s", ecs.ID) - }() - t.Logf("Attempting to update ECSv2: %s", ecs.ID) - ecsName = tools.RandomString("update-ecs-", 3) - updateOpts := servers.UpdateOpts{ + ecsName := tools.RandomString("update-ecs-", 3) + _, err = servers.Update(client, ecs.ID, servers.UpdateOpts{ Name: ecsName, - } - - _, err = servers.Update(client, ecs.ID, updateOpts).Extract() + }).Extract() th.AssertNoErr(t, err) t.Logf("ECSv2 successfully updated: %s", ecs.ID) diff --git a/acceptance/openstack/evs/extensions/extensions.go b/acceptance/openstack/evs/extensions/extensions.go index 15fed9d46..f7b0abbef 100644 --- a/acceptance/openstack/evs/extensions/extensions.go +++ b/acceptance/openstack/evs/extensions/extensions.go @@ -33,7 +33,7 @@ func CreateUploadImage(t *testing.T, client *golangsdk.ServiceClient, volume *vo Force: true, } - volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract() + volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts) if err != nil { return volumeImage, err } @@ -59,7 +59,7 @@ func DeleteUploadedImage(t *testing.T, client *golangsdk.ServiceClient, imageID t.Logf("Removing image %s", imageID) - err := images.Delete(client, imageID).ExtractErr() + err := images.Delete(client, imageID) if err != nil { return err } @@ -82,7 +82,7 @@ func CreateVolumeAttach(t *testing.T, client *golangsdk.ServiceClient, volume *v t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) - if err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr(); err != nil { + if err := volumeactions.Attach(client, volume.ID, attachOpts); err != nil { return err } @@ -104,7 +104,7 @@ func CreateVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume * t.Logf("Attempting to reserve volume %s", volume.ID) - if err := volumeactions.Reserve(client, volume.ID).ExtractErr(); err != nil { + if err := volumeactions.Reserve(client, volume.ID); err != nil { return err } @@ -123,7 +123,7 @@ func DeleteVolumeAttach(t *testing.T, client *golangsdk.ServiceClient, volume *v AttachmentID: volume.Attachments[0].AttachmentID, } - if err := volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr(); err != nil { + if err := volumeactions.Detach(client, volume.ID, detachOpts); err != nil { t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) } @@ -144,7 +144,7 @@ func DeleteVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume * t.Logf("Attempting to unreserve volume %s", volume.ID) - if err := volumeactions.Unreserve(client, volume.ID).ExtractErr(); err != nil { + if err := volumeactions.Unreserve(client, volume.ID); err != nil { t.Fatalf("Unable to unreserve volume %s: %v", volume.ID, err) } @@ -159,7 +159,7 @@ func ExtendVolumeSize(t *testing.T, client *golangsdk.ServiceClient, volume *vol NewSize: 2, } - err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr() + err := volumeactions.ExtendSize(client, volume.ID, extendOpts) if err != nil { return err } @@ -181,7 +181,7 @@ func SetImageMetadata(t *testing.T, client *golangsdk.ServiceClient, volume *vol }, } - err := volumeactions.SetImageMetadata(client, volume.ID, imageMetadataOpts).ExtractErr() + err := volumeactions.SetImageMetadata(client, volume.ID, imageMetadataOpts) if err != nil { return err } @@ -200,7 +200,7 @@ func CreateBackup(t *testing.T, client *golangsdk.ServiceClient, volumeID string Name: backupName, } - backup, err := backups.Create(client, createOpts).Extract() + backup, err := backups.Create(client, createOpts) if err != nil { return nil, err } @@ -210,7 +210,7 @@ func CreateBackup(t *testing.T, client *golangsdk.ServiceClient, volumeID string return nil, err } - backup, err = backups.Get(client, backup.ID).Extract() + backup, err = backups.Get(client, backup.ID) if err != nil { return nil, err } @@ -226,7 +226,7 @@ func CreateBackup(t *testing.T, client *golangsdk.ServiceClient, volumeID string // DeleteBackup will delete a backup. A fatal error will occur if the backup // could not be deleted. This works best when used as a deferred function. func DeleteBackup(t *testing.T, client *golangsdk.ServiceClient, backupID string) { - if err := backups.Delete(client, backupID).ExtractErr(); err != nil { + if err := backups.Delete(client, backupID); err != nil { t.Fatalf("Unable to delete backup %s: %s", backupID, err) } @@ -237,7 +237,7 @@ func DeleteBackup(t *testing.T, client *golangsdk.ServiceClient, backupID string // status. It will do this for the amount of seconds defined. func WaitForBackupStatus(client *golangsdk.ServiceClient, id, status string) error { return tools.WaitFor(func() (bool, error) { - current, err := backups.Get(client, id).Extract() + current, err := backups.Get(client, id) if err != nil { return false, err } @@ -258,12 +258,12 @@ func SetBootable(t *testing.T, client *golangsdk.ServiceClient, volume *volumes. Bootable: true, } - err := volumeactions.SetBootable(client, volume.ID, bootableOpts).ExtractErr() + err := volumeactions.SetBootable(client, volume.ID, bootableOpts) if err != nil { return err } - vol, err := v3.Get(client, volume.ID).Extract() + vol, err := v3.Get(client, volume.ID) if err != nil { return err } @@ -276,12 +276,12 @@ func SetBootable(t *testing.T, client *golangsdk.ServiceClient, volume *volumes. Bootable: false, } - err = volumeactions.SetBootable(client, volume.ID, bootableOpts).ExtractErr() + err = volumeactions.SetBootable(client, volume.ID, bootableOpts) if err != nil { return err } - vol, err = v3.Get(client, volume.ID).Extract() + vol, err = v3.Get(client, volume.ID) if err != nil { return err } @@ -302,7 +302,7 @@ func ChangeVolumeType(t *testing.T, client *golangsdk.ServiceClient, volume *v3. MigrationPolicy: volumeactions.MigrationPolicyOnDemand, } - err := volumeactions.ChangeType(client, volume.ID, changeOpts).ExtractErr() + err := volumeactions.ChangeType(client, volume.ID, changeOpts) if err != nil { return err } @@ -323,7 +323,7 @@ func ReImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volu ReImageReserved: false, } - err := volumeactions.ReImage(client, volume.ID, reimageOpts).ExtractErr() + err := volumeactions.ReImage(client, volume.ID, reimageOpts) if err != nil { return err } @@ -333,7 +333,7 @@ func ReImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volu return err } - vol, err := v3.Get(client, volume.ID).Extract() + vol, err := v3.Get(client, volume.ID) if err != nil { return err } diff --git a/acceptance/openstack/evs/extensions/limits_test.go b/acceptance/openstack/evs/extensions/limits_test.go index 7b76f9842..9cadaedc6 100644 --- a/acceptance/openstack/evs/extensions/limits_test.go +++ b/acceptance/openstack/evs/extensions/limits_test.go @@ -13,7 +13,7 @@ func TestLimits(t *testing.T) { client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - limits, err := limits.Get(client).Extract() + limits, err := limits.Get(client) th.AssertNoErr(t, err) tools.PrintResource(t, limits) diff --git a/acceptance/openstack/evs/extensions/schedulerhints_test.go b/acceptance/openstack/evs/extensions/schedulerhints_test.go index c0d75b13f..d407d1788 100644 --- a/acceptance/openstack/evs/extensions/schedulerhints_test.go +++ b/acceptance/openstack/evs/extensions/schedulerhints_test.go @@ -11,8 +11,6 @@ import ( ) func TestSchedulerHints(t *testing.T) { - clients.RequireLong(t) - client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) @@ -22,12 +20,15 @@ func TestSchedulerHints(t *testing.T) { Name: volumeName, } - volume1, err := volumes.Create(client, createOpts).Extract() + volume1, err := volumes.Create(client, createOpts) th.AssertNoErr(t, err) err = volumes.WaitForStatus(client, volume1.ID, "available", 60) th.AssertNoErr(t, err) - defer volumes.Delete(client, volume1.ID, volumes.DeleteOpts{}) + t.Cleanup(func() { + err := volumes.Delete(client, volumes.DeleteOpts{VolumeId: volume1.ID}) + th.AssertNoErr(t, err) + }) volumeName = tools.RandomString("ACPTTEST", 16) base := volumes.CreateOpts{ @@ -42,16 +43,16 @@ func TestSchedulerHints(t *testing.T) { } createOptsWithHints := schedulerhints.CreateOptsExt{ - VolumeCreateOptsBuilder: base, - SchedulerHints: schedulerHints, + CreateOptsBuilder: base, + SchedulerHints: schedulerHints, } - volume2, err := volumes.Create(client, createOptsWithHints).Extract() + volume2, err := volumes.Create(client, createOptsWithHints) th.AssertNoErr(t, err) err = volumes.WaitForStatus(client, volume2.ID, "available", 60) th.AssertNoErr(t, err) - err = volumes.Delete(client, volume2.ID, volumes.DeleteOpts{}).ExtractErr() + err = volumes.Delete(client, volumes.DeleteOpts{VolumeId: volume2.ID}) th.AssertNoErr(t, err) } diff --git a/acceptance/openstack/evs/extensions/volumeactions_test.go b/acceptance/openstack/evs/extensions/volumeactions_test.go index dd7690479..be5a0aea4 100644 --- a/acceptance/openstack/evs/extensions/volumeactions_test.go +++ b/acceptance/openstack/evs/extensions/volumeactions_test.go @@ -50,7 +50,7 @@ func TestVolumeActionsAttachCreateDestroy(t *testing.T) { err = CreateVolumeAttach(t, blockClient, volume, server) th.AssertNoErr(t, err) - newVolume, err := volumes.Get(blockClient, volume.ID).Extract() + newVolume, err := volumes.Get(blockClient, volume.ID) th.AssertNoErr(t, err) DeleteVolumeAttach(t, blockClient, newVolume) @@ -82,7 +82,7 @@ func TestVolumeActionsExtendSize(t *testing.T) { err = ExtendVolumeSize(t, blockClient, volume) th.AssertNoErr(t, err) - newVolume, err := volumes.Get(blockClient, volume.ID).Extract() + newVolume, err := volumes.Get(blockClient, volume.ID) th.AssertNoErr(t, err) tools.PrintResource(t, newVolume) @@ -135,7 +135,7 @@ func TestVolumeActionsChangeType(t *testing.T) { err = ChangeVolumeType(t, client, volume, volumeType2) th.AssertNoErr(t, err) - newVolume, err := volumes.Get(client, volume.ID).Extract() + newVolume, err := volumes.Get(client, volume.ID) th.AssertNoErr(t, err) th.AssertEquals(t, newVolume.VolumeType, volumeType2.Name) @@ -173,7 +173,7 @@ func TestVolumeConns(t *testing.T) { cv, err := volumes.Create(client, &volumes.CreateOpts{ Size: 1, Name: "blockv2-volume", - }).Extract() + }) th.AssertNoErr(t, err) defer func() { @@ -181,7 +181,7 @@ func TestVolumeConns(t *testing.T) { th.AssertNoErr(t, err) t.Logf("Deleting volume") - err = volumes.Delete(client, cv.ID, volumes.DeleteOpts{}).ExtractErr() + err = volumes.Delete(client, cv.ID, volumes.DeleteOpts{}) th.AssertNoErr(t, err) }() @@ -198,11 +198,11 @@ func TestVolumeConns(t *testing.T) { } t.Logf("Initializing connection") - _, err = volumeactions.InitializeConnection(client, cv.ID, connOpts).Extract() + _, err = volumeactions.InitializeConnection(client, cv.ID, connOpts) th.AssertNoErr(t, err) t.Logf("Terminating connection") - err = volumeactions.TerminateConnection(client, cv.ID, connOpts).ExtractErr() + err = volumeactions.TerminateConnection(client, cv.ID, connOpts) th.AssertNoErr(t, err) } */ diff --git a/acceptance/openstack/evs/extensions/volumetenants_test.go b/acceptance/openstack/evs/extensions/volumetenants_test.go index 6a9fed8e3..6cd011461 100644 --- a/acceptance/openstack/evs/extensions/volumetenants_test.go +++ b/acceptance/openstack/evs/extensions/volumetenants_test.go @@ -35,7 +35,7 @@ func TestVolumeTenants(t *testing.T) { th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, client, volume1) - allPages, err = volumes.List(client, nil).AllPages() + allPages, err = volumes.List(client, volumes.ListOpts{}).AllPages() th.AssertNoErr(t, err) err = volumes.ExtractVolumesInto(allPages, &allVolumes) diff --git a/acceptance/openstack/evs/noauth/blockstorage.go b/acceptance/openstack/evs/noauth/blockstorage.go deleted file mode 100644 index 1bf61fef9..000000000 --- a/acceptance/openstack/evs/noauth/blockstorage.go +++ /dev/null @@ -1,142 +0,0 @@ -// Package noauth contains common functions for creating block storage based -// resources for use in acceptance tests. See the `*_test.go` files for -// example usages. -package noauth - -import ( - "testing" - - golangsdk "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/snapshots" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" -) - -// CreateVolume will create a volume with a random name and size of 1GB. An -// error will be returned if the volume was unable to be created. -func CreateVolume(t *testing.T, client *golangsdk.ServiceClient) (*volumes.Volume, error) { - if testing.Short() { - t.Skip("Skipping test that requires volume creation in short mode.") - } - - volumeName := tools.RandomString("ACPTTEST", 16) - t.Logf("Attempting to create volume: %s", volumeName) - - createOpts := volumes.CreateOpts{ - Size: 1, - Name: volumeName, - } - - volume, err := volumes.Create(client, createOpts).Extract() - if err != nil { - return volume, err - } - - err = volumes.WaitForStatus(client, volume.ID, "available", 60) - if err != nil { - return volume, err - } - - return volume, nil -} - -// CreateVolumeFromImage will create a volume from with a random name and size of -// 1GB. An error will be returned if the volume was unable to be created. -func CreateVolumeFromImage(t *testing.T, client *golangsdk.ServiceClient) (*volumes.Volume, error) { - if testing.Short() { - t.Skip("Skipping test that requires volume creation in short mode.") - } - - choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } - - volumeName := tools.RandomString("ACPTTEST", 16) - t.Logf("Attempting to create volume: %s", volumeName) - - createOpts := volumes.CreateOpts{ - Size: 1, - Name: volumeName, - ImageID: choices.ImageID, - } - - volume, err := volumes.Create(client, createOpts).Extract() - if err != nil { - return volume, err - } - - err = volumes.WaitForStatus(client, volume.ID, "available", 60) - if err != nil { - return volume, err - } - - return volume, nil -} - -// DeleteVolume will delete a volume. A fatal error will occur if the volume -// failed to be deleted. This works best when used as a deferred function. -func DeleteVolume(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) { - err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) - } - - t.Logf("Deleted volume: %s", volume.ID) -} - -// CreateSnapshot will create a snapshot of the specified volume. -// Snapshot will be assigned a random name and description. -func CreateSnapshot(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { - if testing.Short() { - t.Skip("Skipping test that requires snapshot creation in short mode.") - } - - snapshotName := tools.RandomString("ACPTTEST", 16) - snapshotDescription := tools.RandomString("ACPTTEST", 16) - t.Logf("Attempting to create snapshot: %s", snapshotName) - - createOpts := snapshots.CreateOpts{ - VolumeID: volume.ID, - Name: snapshotName, - Description: snapshotDescription, - } - - snapshot, err := snapshots.Create(client, createOpts).Extract() - if err != nil { - return snapshot, err - } - - err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) - if err != nil { - return snapshot, err - } - - return snapshot, nil -} - -// DeleteSnapshot will delete a snapshot. A fatal error will occur if the -// snapshot failed to be deleted. -func DeleteSnapshot(t *testing.T, client *golangsdk.ServiceClient, snapshot *snapshots.Snapshot) { - err := snapshots.Delete(client, snapshot.ID).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) - } - - // Volumes can't be deleted until their snapshots have been, - // so block up to 120 seconds for the snapshot to delete. - err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(client, snapshot.ID).Extract() - if err != nil { - return true, nil - } - - return false, nil - }) - if err != nil { - t.Fatalf("Error waiting for snapshot to delete: %v", err) - } - - t.Logf("Deleted snapshot: %s", snapshot.ID) -} diff --git a/acceptance/openstack/evs/noauth/snapshots_test.go b/acceptance/openstack/evs/noauth/snapshots_test.go deleted file mode 100644 index 562e8e452..000000000 --- a/acceptance/openstack/evs/noauth/snapshots_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package noauth - -import ( - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/snapshots" -) - -func TestSnapshotsList(t *testing.T) { - client, err := clients.NewBlockStorageV3NoAuthClient() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } - - allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve snapshots: %v", err) - } - - allSnapshots, err := snapshots.ExtractSnapshots(allPages) - if err != nil { - t.Fatalf("Unable to extract snapshots: %v", err) - } - - for _, snapshot := range allSnapshots { - tools.PrintResource(t, snapshot) - } -} - -func TestSnapshotsCreateDelete(t *testing.T) { - client, err := clients.NewBlockStorageV3NoAuthClient() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } - - volume, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } - defer DeleteVolume(t, client, volume) - - snapshot, err := CreateSnapshot(t, client, volume) - if err != nil { - t.Fatalf("Unable to create snapshot: %v", err) - } - defer DeleteSnapshot(t, client, snapshot) - - newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve snapshot: %v", err) - } - - tools.PrintResource(t, newSnapshot) -} diff --git a/acceptance/openstack/evs/noauth/volumes_test.go b/acceptance/openstack/evs/noauth/volumes_test.go deleted file mode 100644 index 6afd4f4ed..000000000 --- a/acceptance/openstack/evs/noauth/volumes_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package noauth - -import ( - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" -) - -func TestVolumesList(t *testing.T) { - client, err := clients.NewBlockStorageV3NoAuthClient() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } - - allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve volumes: %v", err) - } - - allVolumes, err := volumes.ExtractVolumes(allPages) - if err != nil { - t.Fatalf("Unable to extract volumes: %v", err) - } - - for _, volume := range allVolumes { - tools.PrintResource(t, volume) - } -} - -func TestVolumesCreateDestroy(t *testing.T) { - client, err := clients.NewBlockStorageV3NoAuthClient() - if err != nil { - t.Fatalf("Unable to create blockstorage client: %v", err) - } - - volume, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } - defer DeleteVolume(t, client, volume) - - newVolume, err := volumes.Get(client, volume.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve volume: %v", err) - } - - tools.PrintResource(t, newVolume) -} diff --git a/acceptance/openstack/evs/v3/blockstorage.go b/acceptance/openstack/evs/v3/blockstorage.go index d75962f3b..183fbf900 100644 --- a/acceptance/openstack/evs/v3/blockstorage.go +++ b/acceptance/openstack/evs/v3/blockstorage.go @@ -28,7 +28,7 @@ func CreateSnapshot(t *testing.T, client *golangsdk.ServiceClient, volume *volum Description: snapshotDescription, } - snapshot, err := snapshots.Create(client, createOpts).Extract() + snapshot, err := snapshots.Create(client, createOpts) if err != nil { return snapshot, err } @@ -60,7 +60,7 @@ func CreateVolume(t *testing.T, client *golangsdk.ServiceClient) (*volumes.Volum Description: volumeDescription, } - volume, err := volumes.Create(client, createOpts).Extract() + volume, err := volumes.Create(client, createOpts) if err != nil { return volume, err } @@ -95,7 +95,7 @@ func CreateVolumeWithType(t *testing.T, client *golangsdk.ServiceClient, vt *vol VolumeType: vt.Name, } - volume, err := volumes.Create(client, createOpts).Extract() + volume, err := volumes.Create(client, createOpts) if err != nil { return volume, err } @@ -129,7 +129,7 @@ func CreateVolumeType(t *testing.T, client *golangsdk.ServiceClient) (*volumetyp Description: description, } - vt, err := volumetypes.Create(client, createOpts).Extract() + vt, err := volumetypes.Create(client, createOpts) if err != nil { return nil, err } @@ -162,7 +162,7 @@ func CreateVolumeTypeNoExtraSpecs(t *testing.T, client *golangsdk.ServiceClient) Description: description, } - vt, err := volumetypes.Create(client, createOpts).Extract() + vt, err := volumetypes.Create(client, createOpts) if err != nil { return nil, err } @@ -193,7 +193,7 @@ func CreatePrivateVolumeType(t *testing.T, client *golangsdk.ServiceClient) (*vo IsPublic: &isPublic, } - vt, err := volumetypes.Create(client, createOpts).Extract() + vt, err := volumetypes.Create(client, createOpts) if err != nil { return nil, err } @@ -211,7 +211,7 @@ func CreatePrivateVolumeType(t *testing.T, client *golangsdk.ServiceClient) (*vo // DeleteSnapshot will delete a snapshot. A fatal error will occur if the // snapshot failed to be deleted. func DeleteSnapshot(t *testing.T, client *golangsdk.ServiceClient, snapshot *snapshots.Snapshot) { - err := snapshots.Delete(client, snapshot.ID).ExtractErr() + err := snapshots.Delete(client, snapshot.ID) if err != nil { t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) } @@ -219,7 +219,7 @@ func DeleteSnapshot(t *testing.T, client *golangsdk.ServiceClient, snapshot *sna // Volumes can't be deleted until their snapshots have been, // so block until the snapshoth as been deleted. err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(client, snapshot.ID).Extract() + _, err := snapshots.Get(client, snapshot.ID) if err != nil { return true, nil } @@ -238,7 +238,7 @@ func DeleteSnapshot(t *testing.T, client *golangsdk.ServiceClient, snapshot *sna func DeleteVolume(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) { t.Logf("Attempting to delete volume: %s", volume.ID) - err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr() + err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}) if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } @@ -246,7 +246,7 @@ func DeleteVolume(t *testing.T, client *golangsdk.ServiceClient, volume *volumes // VolumeTypes can't be deleted until their volumes have been, // so block until the volume is deleted. err = tools.WaitFor(func() (bool, error) { - _, err := volumes.Get(client, volume.ID).Extract() + _, err := volumes.Get(client, volume.ID) if err != nil { return true, nil } @@ -266,7 +266,7 @@ func DeleteVolume(t *testing.T, client *golangsdk.ServiceClient, volume *volumes func DeleteVolumeType(t *testing.T, client *golangsdk.ServiceClient, vt *volumetypes.VolumeType) { t.Logf("Attempting to delete volume type: %s", vt.ID) - err := volumetypes.Delete(client, vt.ID).ExtractErr() + err := volumetypes.Delete(client, vt.ID) if err != nil { t.Fatalf("Unable to delete volume %s: %v", vt.ID, err) } @@ -288,7 +288,7 @@ func CreateQoS(t *testing.T, client *golangsdk.ServiceClient) (*qos.QoS, error) }, } - qs, err := qos.Create(client, createOpts).Extract() + qs, err := qos.Create(client, createOpts) if err != nil { return nil, err } @@ -312,7 +312,7 @@ func DeleteQoS(t *testing.T, client *golangsdk.ServiceClient, qs *qos.QoS) { Force: true, } - err := qos.Delete(client, qs.ID, deleteOpts).ExtractErr() + err := qos.Delete(client, qs.ID, deleteOpts) if err != nil { t.Fatalf("Unable to delete QoS %s: %v", qs.ID, err) } diff --git a/acceptance/openstack/evs/v3/qos_test.go b/acceptance/openstack/evs/v3/qos_test.go index fc4540ca8..96487383a 100644 --- a/acceptance/openstack/evs/v3/qos_test.go +++ b/acceptance/openstack/evs/v3/qos_test.go @@ -24,11 +24,11 @@ func TestQoS(t *testing.T) { th.AssertNoErr(t, err) defer DeleteQoS(t, client, qos2) - getQoS2, err := qos.Get(client, qos2.ID).Extract() + getQoS2, err := qos.Get(client, qos2.ID) th.AssertNoErr(t, err) th.AssertDeepEquals(t, qos2, getQoS2) - err = qos.DeleteKeys(client, qos2.ID, qos.DeleteKeysOpts{"read_iops_sec"}).ExtractErr() + err = qos.DeleteKeys(client, qos2.ID, qos.DeleteKeysOpts{"read_iops_sec"}) th.AssertNoErr(t, err) updateOpts := qos.UpdateOpts{ @@ -45,7 +45,7 @@ func TestQoS(t *testing.T) { "write_iops_sec": "40000", } - updatedQosSpecs, err := qos.Update(client, qos2.ID, updateOpts).Extract() + updatedQosSpecs, err := qos.Update(client, qos2.ID, updateOpts) th.AssertNoErr(t, err) th.AssertDeepEquals(t, updatedQosSpecs, expectedQosSpecs) @@ -92,7 +92,7 @@ func TestQoSAssociations(t *testing.T) { VolumeTypeID: vt.ID, } - err = qos.Associate(client, qos1.ID, associateOpts).ExtractErr() + err = qos.Associate(client, qos1.ID, associateOpts) th.AssertNoErr(t, err) allQosAssociations, err := qos.ListAssociations(client, qos1.ID).AllPages() @@ -108,7 +108,7 @@ func TestQoSAssociations(t *testing.T) { VolumeTypeID: vt.ID, } - err = qos.Disassociate(client, qos1.ID, disassociateOpts).ExtractErr() + err = qos.Disassociate(client, qos1.ID, disassociateOpts) th.AssertNoErr(t, err) allQosAssociations, err = qos.ListAssociations(client, qos1.ID).AllPages() @@ -119,9 +119,9 @@ func TestQoSAssociations(t *testing.T) { tools.PrintResource(t, allAssociations) th.AssertEquals(t, 0, len(allAssociations)) - err = qos.Associate(client, qos1.ID, associateOpts).ExtractErr() + err = qos.Associate(client, qos1.ID, associateOpts) th.AssertNoErr(t, err) - err = qos.DisassociateAll(client, qos1.ID).ExtractErr() + err = qos.DisassociateAll(client, qos1.ID) th.AssertNoErr(t, err) } diff --git a/acceptance/openstack/evs/v3/quotaset_test.go b/acceptance/openstack/evs/v3/quotaset_test.go index 5d8948655..6eb13d659 100644 --- a/acceptance/openstack/evs/v3/quotaset_test.go +++ b/acceptance/openstack/evs/v3/quotaset_test.go @@ -17,7 +17,7 @@ func TestQuotasetGet(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSet, err := quotasets.Get(client, projectID).Extract() + quotaSet, err := quotasets.Get(client, projectID) th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) @@ -28,7 +28,7 @@ func TestQuotasetGetDefaults(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSet, err := quotasets.GetDefaults(client, projectID).Extract() + quotaSet, err := quotasets.GetDefaults(client, projectID) th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) @@ -39,7 +39,7 @@ func TestQuotasetGetUsage(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSetUsage, err := quotasets.GetUsage(client, projectID).Extract() + quotaSetUsage, err := quotasets.GetUsage(client, projectID) th.AssertNoErr(t, err) tools.PrintResource(t, quotaSetUsage) @@ -82,32 +82,32 @@ func TestQuotasetUpdate(t *testing.T) { client, projectID := getClientAndProject(t) // save original quotas - orig, err := quotasets.Get(client, projectID).Extract() + orig, err := quotasets.Get(client, projectID) th.AssertNoErr(t, err) // create volumeType to test volume type quota - volumeType, err := volumetypes.Create(client, VolumeTypeCreateOpts).Extract() + volumeType, err := volumetypes.Create(client, VolumeTypeCreateOpts) th.AssertNoErr(t, err) defer func() { restore := quotasets.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) - err := volumetypes.Delete(client, volumeType.ID).ExtractErr() + err := volumetypes.Delete(client, volumeType.ID) th.AssertNoErr(t, err) - _, err = quotasets.Update(client, projectID, restore).Extract() + _, err = quotasets.Update(client, projectID, restore) th.AssertNoErr(t, err) }() // test Update - resultQuotas, err := quotasets.Update(client, projectID, UpdateQuotaOpts).Extract() + resultQuotas, err := quotasets.Update(client, projectID, UpdateQuotaOpts) th.AssertNoErr(t, err) // We dont know the default quotas, so just check if the quotas are not the // same as before - newQuotas, err := quotasets.Get(client, projectID).Extract() + newQuotas, err := quotasets.Get(client, projectID) th.AssertNoErr(t, err) th.AssertEquals(t, resultQuotas.Volumes, newQuotas.Volumes) th.AssertEquals(t, resultQuotas.Extra["volumes_foo"], newQuotas.Extra["volumes_foo"]) @@ -140,26 +140,26 @@ func TestQuotasetDelete(t *testing.T) { client, projectID := getClientAndProject(t) // save original quotas - orig, err := quotasets.Get(client, projectID).Extract() + orig, err := quotasets.Get(client, projectID) th.AssertNoErr(t, err) defer func() { restore := quotasets.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) - _, err = quotasets.Update(client, projectID, restore).Extract() + _, err = quotasets.Update(client, projectID, restore) th.AssertNoErr(t, err) }() // Obtain environment default quotaset values to validate deletion. - defaultQuotaSet, err := quotasets.GetDefaults(client, projectID).Extract() + defaultQuotaSet, err := quotasets.GetDefaults(client, projectID) th.AssertNoErr(t, err) // Test Delete - err = quotasets.Delete(client, projectID).ExtractErr() + err = quotasets.Delete(client, projectID) th.AssertNoErr(t, err) - newQuotas, err := quotasets.Get(client, projectID).Extract() + newQuotas, err := quotasets.Get(client, projectID) th.AssertNoErr(t, err) th.AssertEquals(t, newQuotas.Volumes, defaultQuotaSet.Volumes) diff --git a/acceptance/openstack/evs/v3/snapshots_test.go b/acceptance/openstack/evs/v3/snapshots_test.go index d8eb3abe9..f921d813e 100644 --- a/acceptance/openstack/evs/v3/snapshots_test.go +++ b/acceptance/openstack/evs/v3/snapshots_test.go @@ -32,7 +32,7 @@ func TestSnapshots(t *testing.T) { Description: &updatedSnapshotDescription, } t.Logf("Attempting to update snapshot: %s", updatedSnapshotName) - updatedSnapshot, err := snapshots.Update(client, snapshot1.ID, updateOpts).Extract() + updatedSnapshot, err := snapshots.Update(client, snapshot1.ID, updateOpts) th.AssertNoErr(t, err) tools.PrintResource(t, updatedSnapshot) diff --git a/acceptance/openstack/evs/v3/volumeattachments.go b/acceptance/openstack/evs/v3/volumeattachments.go index 6c8c53d30..01305e517 100644 --- a/acceptance/openstack/evs/v3/volumeattachments.go +++ b/acceptance/openstack/evs/v3/volumeattachments.go @@ -30,7 +30,7 @@ func CreateVolumeAttachment(t *testing.T, client *golangsdk.ServiceClient, volum var err error var attachment *attachments.Attachment - if attachment, err = attachments.Create(client, attachOpts).Extract(); err != nil { + if attachment, err = attachments.Create(client, attachOpts); err != nil { return err } @@ -39,19 +39,19 @@ func CreateVolumeAttachment(t *testing.T, client *golangsdk.ServiceClient, volum defer func() { client.Microversion = mv }() - if err = attachments.Complete(client, attachment.ID).ExtractErr(); err != nil { + if err = attachments.Complete(client, attachment.ID); err != nil { return err } if err = attachments.WaitForStatus(client, attachment.ID, "attached", 60); err != nil { - e := attachments.Delete(client, attachment.ID).ExtractErr() + e := attachments.Delete(client, attachment.ID) if e != nil { t.Logf("Failed to delete %q attachment: %s", attachment.ID, err) } return err } - attachment, err = attachments.Get(client, attachment.ID).Extract() + attachment, err = attachments.Get(client, attachment.ID) if err != nil { return err } @@ -64,7 +64,7 @@ func CreateVolumeAttachment(t *testing.T, client *golangsdk.ServiceClient, volum "initiator": "fake", }, } - attachment, err = attachments.Update(client, attachment.ID, updateOpts).Extract() + attachment, err = attachments.Update(client, attachment.ID, updateOpts) if err != nil { return err } @@ -98,7 +98,7 @@ func CreateVolumeAttachment(t *testing.T, client *golangsdk.ServiceClient, volum func DeleteVolumeAttachment(t *testing.T, client *golangsdk.ServiceClient, volume *v3.Volume) { t.Logf("Attepting to detach volume volume: %s", volume.ID) - if err := attachments.Delete(client, volume.Attachments[0].AttachmentID).ExtractErr(); err != nil { + if err := attachments.Delete(client, volume.Attachments[0].AttachmentID); err != nil { t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) } diff --git a/acceptance/openstack/evs/v3/volumeattachments_test.go b/acceptance/openstack/evs/v3/volumeattachments_test.go index b6c2a711a..f37f7ce2b 100644 --- a/acceptance/openstack/evs/v3/volumeattachments_test.go +++ b/acceptance/openstack/evs/v3/volumeattachments_test.go @@ -21,16 +21,16 @@ func TestVolumeAttachments(t *testing.T) { server, err := compute.CreateServer(t, computeClient) th.AssertNoErr(t, err) - defer compute.DeleteServer(t, computeClient, server) + t.Cleanup(func() { compute.DeleteServer(t, computeClient, server) }) volume, err := CreateVolume(t, blockClient) th.AssertNoErr(t, err) - defer DeleteVolume(t, blockClient, volume) + t.Cleanup(func() { DeleteVolume(t, blockClient, volume) }) err = CreateVolumeAttachment(t, blockClient, volume, server) th.AssertNoErr(t, err) - newVolume, err := volumes.Get(blockClient, volume.ID).Extract() + newVolume, err := volumes.Get(blockClient, volume.ID) th.AssertNoErr(t, err) DeleteVolumeAttachment(t, blockClient, newVolume) diff --git a/acceptance/openstack/evs/v3/volumes_test.go b/acceptance/openstack/evs/v3/volumes_test.go index 14957a08e..06a47a106 100644 --- a/acceptance/openstack/evs/v3/volumes_test.go +++ b/acceptance/openstack/evs/v3/volumes_test.go @@ -12,27 +12,25 @@ import ( ) func TestVolumes(t *testing.T) { - clients.RequireLong(t) - client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volume1, err := CreateVolume(t, client) th.AssertNoErr(t, err) - defer DeleteVolume(t, client, volume1) + t.Cleanup(func() { DeleteVolume(t, client, volume1) }) volume2, err := CreateVolume(t, client) th.AssertNoErr(t, err) - defer DeleteVolume(t, client, volume2) + t.Cleanup(func() { DeleteVolume(t, client, volume2) }) // Update volume updatedVolumeName := "" updatedVolumeDescription := "" - updateOpts := volumes.UpdateOpts{ - Name: &updatedVolumeName, - Description: &updatedVolumeDescription, - } - updatedVolume, err := volumes.Update(client, volume1.ID, updateOpts).Extract() + updatedVolume, err := volumes.Update(client, volumes.UpdateOpts{ + VolumeId: volume1.ID, + Name: updatedVolumeName, + Description: updatedVolumeDescription, + }) th.AssertNoErr(t, err) tools.PrintResource(t, updatedVolume) @@ -64,8 +62,6 @@ func TestVolumes(t *testing.T) { } func TestVolumesMultiAttach(t *testing.T) { - clients.RequireLong(t) - client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) @@ -78,7 +74,7 @@ func TestVolumesMultiAttach(t *testing.T) { Multiattach: true, } - vol, err := volumes.Create(client, volOpts).Extract() + vol, err := volumes.Create(client, volOpts) th.AssertNoErr(t, err) err = volumes.WaitForStatus(client, vol.ID, "available", 60) @@ -86,13 +82,11 @@ func TestVolumesMultiAttach(t *testing.T) { th.AssertEquals(t, vol.Multiattach, true) - err = volumes.Delete(client, vol.ID, volumes.DeleteOpts{}).ExtractErr() + err = volumes.Delete(client, volumes.DeleteOpts{VolumeId: vol.ID}) th.AssertNoErr(t, err) } func TestVolumesCascadeDelete(t *testing.T) { - clients.RequireLong(t) - client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) @@ -110,15 +104,17 @@ func TestVolumesCascadeDelete(t *testing.T) { t.Logf("Attempting to delete volume: %s", vol.ID) - deleteOpts := volumes.DeleteOpts{Cascade: true} - err = volumes.Delete(client, vol.ID, deleteOpts).ExtractErr() + err = volumes.Delete(client, volumes.DeleteOpts{ + VolumeId: vol.ID, + Cascade: true, + }) if err != nil { t.Fatalf("Unable to delete volume %s: %v", vol.ID, err) } for _, sid := range []string{snapshot1.ID, snapshot2.ID} { err := tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(client, sid).Extract() + _, err := snapshots.Get(client, sid) if err != nil { return true, nil } @@ -129,7 +125,7 @@ func TestVolumesCascadeDelete(t *testing.T) { } err = tools.WaitFor(func() (bool, error) { - _, err := volumes.Get(client, vol.ID).Extract() + _, err := volumes.Get(client, vol.ID) if err != nil { return true, nil } @@ -138,5 +134,4 @@ func TestVolumesCascadeDelete(t *testing.T) { th.AssertNoErr(t, err) t.Logf("Successfully deleted volume: %s", vol.ID) - } diff --git a/acceptance/openstack/evs/v3/volumetypes_test.go b/acceptance/openstack/evs/v3/volumetypes_test.go index f518356d3..a9763eca6 100644 --- a/acceptance/openstack/evs/v3/volumetypes_test.go +++ b/acceptance/openstack/evs/v3/volumetypes_test.go @@ -45,7 +45,7 @@ func TestVolumeTypes(t *testing.T) { IsPublic: &isPublic, } - newVT, err := volumetypes.Update(client, vt.ID, updateOpts).Extract() + newVT, err := volumetypes.Update(client, vt.ID, updateOpts) th.AssertNoErr(t, err) tools.PrintResource(t, newVT) @@ -69,7 +69,7 @@ func TestVolumeTypesExtraSpecs(t *testing.T) { "volume_backend_name": "ssd", } - createdExtraSpecs, err := volumetypes.CreateExtraSpecs(client, vt.ID, createOpts).Extract() + createdExtraSpecs, err := volumetypes.CreateExtraSpecs(client, vt.ID, createOpts) th.AssertNoErr(t, err) tools.PrintResource(t, createdExtraSpecs) @@ -78,20 +78,20 @@ func TestVolumeTypesExtraSpecs(t *testing.T) { th.AssertEquals(t, createdExtraSpecs["capabilities"], "gpu") th.AssertEquals(t, createdExtraSpecs["volume_backend_name"], "ssd") - err = volumetypes.DeleteExtraSpec(client, vt.ID, "volume_backend_name").ExtractErr() + err = volumetypes.DeleteExtraSpec(client, vt.ID, "volume_backend_name") th.AssertNoErr(t, err) updateOpts := volumetypes.ExtraSpecsOpts{ "capabilities": "gpu-2", } - updatedExtraSpec, err := volumetypes.UpdateExtraSpec(client, vt.ID, updateOpts).Extract() + updatedExtraSpec, err := volumetypes.UpdateExtraSpec(client, vt.ID, updateOpts) th.AssertNoErr(t, err) tools.PrintResource(t, updatedExtraSpec) th.AssertEquals(t, updatedExtraSpec["capabilities"], "gpu-2") - allExtraSpecs, err := volumetypes.ListExtraSpecs(client, vt.ID).Extract() + allExtraSpecs, err := volumetypes.ListExtraSpecs(client, vt.ID) th.AssertNoErr(t, err) tools.PrintResource(t, allExtraSpecs) @@ -99,7 +99,7 @@ func TestVolumeTypesExtraSpecs(t *testing.T) { th.AssertEquals(t, len(allExtraSpecs), 1) th.AssertEquals(t, allExtraSpecs["capabilities"], "gpu-2") - singleSpec, err := volumetypes.GetExtraSpec(client, vt.ID, "capabilities").Extract() + singleSpec, err := volumetypes.GetExtraSpec(client, vt.ID, "capabilities") th.AssertNoErr(t, err) tools.PrintResource(t, singleSpec) @@ -128,7 +128,7 @@ func TestVolumeTypesAccess(t *testing.T) { Project: project.ID, } - err = volumetypes.AddAccess(client, vt.ID, addAccessOpts).ExtractErr() + err = volumetypes.AddAccess(client, vt.ID, addAccessOpts) th.AssertNoErr(t, err) allPages, err := volumetypes.ListAccesses(client, vt.ID).AllPages() @@ -147,7 +147,7 @@ func TestVolumeTypesAccess(t *testing.T) { Project: project.ID, } - err = volumetypes.RemoveAccess(client, vt.ID, removeAccessOpts).ExtractErr() + err = volumetypes.RemoveAccess(client, vt.ID, removeAccessOpts) th.AssertNoErr(t, err) allPages, err = volumetypes.ListAccesses(client, vt.ID).AllPages() diff --git a/openstack/evs/extensions/schedulerhints/requests.go b/openstack/evs/extensions/schedulerhints/requests.go index 806768c5c..c4d070881 100644 --- a/openstack/evs/extensions/schedulerhints/requests.go +++ b/openstack/evs/extensions/schedulerhints/requests.go @@ -4,6 +4,7 @@ import ( "regexp" "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" ) // SchedulerHints represents a set of scheduling hints that are passed to the @@ -12,33 +13,18 @@ type SchedulerHints struct { // DifferentHost will place the volume on a different back-end that does not // host the given volumes. DifferentHost []string - // SameHost will place the volume on a back-end that hosts the given volumes. SameHost []string - // LocalToInstance will place volume on same host on a given instance LocalToInstance string - // Query is a conditional statement that results in back-ends able to // host the volume. Query string - // AdditionalProperies are arbitrary key/values that are not validated by nova. AdditionalProperties map[string]interface{} } -// VolumeCreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type VolumeCreateOptsBuilder interface { - ToVolumeCreateMap() (map[string]interface{}, error) -} - -// CreateOptsBuilder builds the scheduler hints into a serializable format. -type CreateOptsBuilder interface { - ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) -} - -// ToVolumeSchedulerHintsMap builds the scheduler hints into a serializable format. +// ToVolumeSchedulerHintsCreateMap builds the scheduler hints into a serializable format. func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) { sh := make(map[string]interface{}) @@ -96,15 +82,14 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa // CreateOptsExt adds a SchedulerHints option to the base CreateOpts. type CreateOptsExt struct { - VolumeCreateOptsBuilder - + volumes.CreateOptsBuilder // SchedulerHints provides a set of hints to the scheduler. - SchedulerHints CreateOptsBuilder + SchedulerHints SchedulerHints } // ToVolumeCreateMap adds the SchedulerHints option to the base volume creation options. func (opts CreateOptsExt) ToVolumeCreateMap() (map[string]interface{}, error) { - base, err := opts.VolumeCreateOptsBuilder.ToVolumeCreateMap() + base, err := opts.CreateOptsBuilder.ToVolumeCreateMap() if err != nil { return nil, err } diff --git a/openstack/evs/v3/volumes/Create.go b/openstack/evs/v3/volumes/Create.go index 57c2f59e7..6bac108bb 100644 --- a/openstack/evs/v3/volumes/Create.go +++ b/openstack/evs/v3/volumes/Create.go @@ -2,9 +2,12 @@ package volumes import ( "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/internal/build" ) +type CreateOptsBuilder interface { + ToVolumeCreateMap() (map[string]interface{}, error) +} + // CreateOpts contains options for creating a Volume. This object is passed to // the volumes.Create function. For more information about these parameters, see the Volume object. type CreateOpts struct { @@ -97,9 +100,13 @@ type Metadata struct { FullClone string `json:"full_clone"` } +func (c CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { + return golangsdk.BuildRequestBody(c, "volume") +} + // Create will create a new Volume based on the values in CreateOpts. -func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Volume, error) { - b, err := build.RequestBody(opts, "volume") +func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (*Volume, error) { + b, err := opts.ToVolumeCreateMap() if err != nil { return nil, err } diff --git a/openstack/evs/v3/volumes/List.go b/openstack/evs/v3/volumes/List.go index 25867ea4f..9ce0a12df 100644 --- a/openstack/evs/v3/volumes/List.go +++ b/openstack/evs/v3/volumes/List.go @@ -2,6 +2,7 @@ package volumes import ( "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) @@ -41,3 +42,37 @@ func List(client *golangsdk.ServiceClient, opts ListOpts) pagination.Pager { return VolumePage{pagination.LinkedPageBase{PageResult: r}} }) } + +// VolumePage is a pagination.pager that is returned from a call to the List function. +type VolumePage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a ListResult contains no Volumes. +func (r VolumePage) IsEmpty() (bool, error) { + volumes, err := ExtractVolumes(r) + return len(volumes) == 0, err +} + +func (r VolumePage) NextPageURL() (string, error) { + var s struct { + Links []golangsdk.Link `json:"volumes_links"` + } + err := extract.Into(r.BodyReader(), &s) + if err != nil { + return "", err + } + return golangsdk.ExtractNextURL(s.Links) +} + +// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. +func ExtractVolumes(r pagination.Page) ([]Volume, error) { + var s []Volume + err := ExtractVolumesInto(r, &s) + return s, err +} + +// ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes +func ExtractVolumesInto(r pagination.Page, v interface{}) error { + return extract.IntoStructPtr(r.(VolumePage).Result.BodyReader(), v, "volumes") +} diff --git a/openstack/evs/v3/volumes/Update.go b/openstack/evs/v3/volumes/Update.go index 60967043b..c3e5f7a82 100644 --- a/openstack/evs/v3/volumes/Update.go +++ b/openstack/evs/v3/volumes/Update.go @@ -9,6 +9,7 @@ import ( // to the volumes.Update function. For more information about the parameters, see // the Volume object. type UpdateOpts struct { + VolumeId string // Specifies the disk name. The value can contain a maximum of 255 bytes. Name string `json:"name,omitempty"` // Specifies the disk description. The value can contain a maximum of 255 bytes. @@ -26,13 +27,14 @@ type UpdateOpts struct { // Update will update the Volume with provided information. To extract the updated // Volume from the response, call the Extract method on the UpdateResult. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOpts) (*Volume, error) { +func Update(client *golangsdk.ServiceClient, opts UpdateOpts) (*Volume, error) { b, err := build.RequestBody(opts, "volume") if err != nil { return nil, err } - raw, err := client.Put(client.ServiceURL("volumes", id), b, nil, &golangsdk.RequestOpts{ + // PUT /v3/{project_id}/volumes/{volume_id} + raw, err := client.Put(client.ServiceURL("volumes", opts.VolumeId), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) return extra(err, raw) diff --git a/openstack/evs/v3/volumes/results.go b/openstack/evs/v3/volumes/results.go index 6a949c454..8f1b724dc 100644 --- a/openstack/evs/v3/volumes/results.go +++ b/openstack/evs/v3/volumes/results.go @@ -7,7 +7,6 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Attachment represents a Volume Attachment record @@ -138,81 +137,11 @@ func extra(err error, raw *http.Response) (*Volume, error) { return &res, err } -// VolumePage is a pagination.pager that is returned from a call to the List function. -type VolumePage struct { - pagination.LinkedPageBase -} - -// IsEmpty returns true if a ListResult contains no Volumes. -func (r VolumePage) IsEmpty() (bool, error) { - volumes, err := ExtractVolumes(r) - return len(volumes) == 0, err -} - -func (page VolumePage) NextPageURL() (string, error) { - var s struct { - Links []golangsdk.Link `json:"volumes_links"` - } - err := page.ExtractInto(&s) - if err != nil { - return "", err - } - return golangsdk.ExtractNextURL(s.Links) -} - -// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. -func ExtractVolumes(r pagination.Page) ([]Volume, error) { - var s []Volume - err := ExtractVolumesInto(r, &s) - return s, err -} - -type commonResult struct { - golangsdk.Result -} - -// Extract will get the Volume object out of the commonResult object. -func (r commonResult) Extract() (*Volume, error) { - var s Volume - err := r.ExtractInto(&s) - return &s, err -} - -// ExtractInto converts our response data into a volume struct -func (r commonResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "volume") -} - -// ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes -func ExtractVolumesInto(r pagination.Page, v interface{}) error { - return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes") -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// UpdateResult contains the response body and error from an Update request. -type UpdateResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - golangsdk.ErrResult -} - // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { return golangsdk.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() + current, err := Get(c, id) if err != nil { return false, err } From 7bb85704957d412540fa04748e854e7eac6efd1d Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 2 Nov 2022 17:32:05 +0100 Subject: [PATCH 12/51] extract --- openstack/evs/v3/attachments/requests.go | 12 +- openstack/evs/v3/attachments/urls.go | 27 -- openstack/evs/v3/qos/requests.go | 20 +- openstack/evs/v3/qos/urls.go | 43 --- openstack/evs/v3/snapshots/requests.go | 12 +- openstack/evs/v3/snapshots/urls.go | 31 -- openstack/evs/v3/volumetypes/AddAccess.go | 34 ++ openstack/evs/v3/volumetypes/Create.go | 45 +++ .../evs/v3/volumetypes/CreateExtraSpecs.go | 33 ++ openstack/evs/v3/volumetypes/Delete.go | 10 + .../evs/v3/volumetypes/DeleteExtraSpec.go | 13 + openstack/evs/v3/volumetypes/Get.go | 11 + openstack/evs/v3/volumetypes/GetExtraSpec.go | 10 + openstack/evs/v3/volumetypes/List.go | 49 +++ openstack/evs/v3/volumetypes/ListAccesses.go | 15 + .../evs/v3/volumetypes/ListExtraSpecs.go | 12 + openstack/evs/v3/volumetypes/RemoveAccess.go | 34 ++ openstack/evs/v3/volumetypes/Update.go | 39 +++ .../evs/v3/volumetypes/UpdateExtraSpec.go | 42 +++ openstack/evs/v3/volumetypes/requests.go | 306 ------------------ openstack/evs/v3/volumetypes/urls.go | 51 --- 21 files changed, 369 insertions(+), 480 deletions(-) delete mode 100644 openstack/evs/v3/attachments/urls.go delete mode 100644 openstack/evs/v3/qos/urls.go delete mode 100644 openstack/evs/v3/snapshots/urls.go create mode 100644 openstack/evs/v3/volumetypes/AddAccess.go create mode 100644 openstack/evs/v3/volumetypes/Create.go create mode 100644 openstack/evs/v3/volumetypes/CreateExtraSpecs.go create mode 100644 openstack/evs/v3/volumetypes/Delete.go create mode 100644 openstack/evs/v3/volumetypes/DeleteExtraSpec.go create mode 100644 openstack/evs/v3/volumetypes/Get.go create mode 100644 openstack/evs/v3/volumetypes/GetExtraSpec.go create mode 100644 openstack/evs/v3/volumetypes/List.go create mode 100644 openstack/evs/v3/volumetypes/ListAccesses.go create mode 100644 openstack/evs/v3/volumetypes/ListExtraSpecs.go create mode 100644 openstack/evs/v3/volumetypes/RemoveAccess.go create mode 100644 openstack/evs/v3/volumetypes/Update.go create mode 100644 openstack/evs/v3/volumetypes/UpdateExtraSpec.go delete mode 100644 openstack/evs/v3/volumetypes/requests.go delete mode 100644 openstack/evs/v3/volumetypes/urls.go diff --git a/openstack/evs/v3/attachments/requests.go b/openstack/evs/v3/attachments/requests.go index 712fa1a61..1edb58bcf 100644 --- a/openstack/evs/v3/attachments/requests.go +++ b/openstack/evs/v3/attachments/requests.go @@ -46,7 +46,7 @@ func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateRe r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ + resp, err := client.Post(client.ServiceURL("attachments"), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -55,7 +55,7 @@ func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateRe // Delete will delete the existing Attachment with the provided ID. func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), &golangsdk.RequestOpts{ + resp, err := client.Delete(client.ServiceURL("attachments", id), &golangsdk.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -65,7 +65,7 @@ func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { // Get retrieves the Attachment with the provided ID. To extract the Attachment // object from the response, call the Extract method on the GetResult. func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(client.ServiceURL("attachments", id), &r.Body, nil) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -117,7 +117,7 @@ func (opts ListOpts) ToAttachmentListQuery() (string, error) { // List returns Attachments optionally limited by the conditions provided in // ListOpts. func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) + url := client.ServiceURL("attachments", "detail") if opts != nil { query, err := opts.ToAttachmentListQuery() if err != nil { @@ -159,7 +159,7 @@ func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ + resp, err := client.Put(client.ServiceURL("attachments", id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -172,7 +172,7 @@ func Complete(client *golangsdk.ServiceClient, id string) (r CompleteResult) { b := map[string]interface{}{ "os-complete": nil, } - resp, err := client.Post(completeURL(client, id), b, nil, &golangsdk.RequestOpts{ + resp, err := client.Post(client.ServiceURL("attachments", id, "action"), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) diff --git a/openstack/evs/v3/attachments/urls.go b/openstack/evs/v3/attachments/urls.go deleted file mode 100644 index dfa2b986f..000000000 --- a/openstack/evs/v3/attachments/urls.go +++ /dev/null @@ -1,27 +0,0 @@ -package attachments - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("attachments") -} - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("attachments", "detail") -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("attachments", id) -} - -func updateURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("attachments", id) -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("attachments", id) -} - -func completeURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("attachments", id, "action") -} diff --git a/openstack/evs/v3/qos/requests.go b/openstack/evs/v3/qos/requests.go index 10e1933f8..fd5635d0c 100644 --- a/openstack/evs/v3/qos/requests.go +++ b/openstack/evs/v3/qos/requests.go @@ -64,7 +64,7 @@ func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateRe r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ + resp, err := client.Post(client.ServiceURL("qos-specs"), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -92,7 +92,7 @@ func (opts DeleteOpts) ToQoSDeleteQuery() (string, error) { // Delete will delete the existing QoS with the provided ID. func Delete(client *golangsdk.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { - url := deleteURL(client, id) + url := client.ServiceURL("qos-specs", id) if opts != nil { query, err := opts.ToQoSDeleteQuery() if err != nil { @@ -131,7 +131,7 @@ func (opts ListOpts) ToQoSListQuery() (string, error) { // You may provide criteria by which List curtails its results for easier // processing. func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) + url := client.ServiceURL("qos-specs") if opts != nil { query, err := opts.ToQoSListQuery() if err != nil { @@ -147,7 +147,7 @@ func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Page // Get retrieves details of a single qos. Use Extract to convert its // result into a QoS. func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &golangsdk.RequestOpts{ + resp, err := client.Get(client.ServiceURL("qos-specs", id), &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -203,7 +203,7 @@ func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ + resp, err := client.Put(client.ServiceURL("qos-specs", id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -232,7 +232,7 @@ func DeleteKeys(client *golangsdk.ServiceClient, qosID string, opts DeleteKeysOp r.Err = err return } - resp, err := client.Put(deleteKeysURL(client, qosID), b, nil, &golangsdk.RequestOpts{ + resp, err := client.Put(client.ServiceURL("qos-specs", qosID, "delete_keys"), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -259,7 +259,7 @@ func (opts AssociateOpts) ToQosAssociateQuery() (string, error) { // Associate will associate a qos with a volute type func Associate(client *golangsdk.ServiceClient, qosID string, opts AssociateOptsBuilder) (r AssociateResult) { - url := associateURL(client, qosID) + url := client.ServiceURL("qos-specs", qosID, "associate") query, err := opts.ToQosAssociateQuery() if err != nil { r.Err = err @@ -294,7 +294,7 @@ func (opts DisassociateOpts) ToQosDisassociateQuery() (string, error) { // Disassociate will disassociate a qos from a volute type func Disassociate(client *golangsdk.ServiceClient, qosID string, opts DisassociateOptsBuilder) (r DisassociateResult) { - url := disassociateURL(client, qosID) + url := client.ServiceURL("qos-specs", qosID, "disassociate") query, err := opts.ToQosDisassociateQuery() if err != nil { r.Err = err @@ -311,7 +311,7 @@ func Disassociate(client *golangsdk.ServiceClient, qosID string, opts Disassocia // DisassociateAll will disassociate a qos from all volute types func DisassociateAll(client *golangsdk.ServiceClient, qosID string) (r DisassociateAllResult) { - resp, err := client.Get(disassociateAllURL(client, qosID), nil, &golangsdk.RequestOpts{ + resp, err := client.Get(client.ServiceURL("qos-specs", qosID, "disassociate_all"), nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -320,7 +320,7 @@ func DisassociateAll(client *golangsdk.ServiceClient, qosID string) (r Disassoci // ListAssociations retrieves the associations of a QoS. func ListAssociations(client *golangsdk.ServiceClient, qosID string) pagination.Pager { - url := listAssociationsURL(client, qosID) + url := client.ServiceURL("qos-specs", qosID, "associations") return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return AssociationPage{pagination.SinglePageBase(r)} diff --git a/openstack/evs/v3/qos/urls.go b/openstack/evs/v3/qos/urls.go deleted file mode 100644 index 9be3accc3..000000000 --- a/openstack/evs/v3/qos/urls.go +++ /dev/null @@ -1,43 +0,0 @@ -package qos - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func getURL(client *golangsdk.ServiceClient, id string) string { - return client.ServiceURL("qos-specs", id) -} - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("qos-specs") -} - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("qos-specs") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("qos-specs", id) -} - -func updateURL(client *golangsdk.ServiceClient, id string) string { - return client.ServiceURL("qos-specs", id) -} - -func deleteKeysURL(client *golangsdk.ServiceClient, id string) string { - return client.ServiceURL("qos-specs", id, "delete_keys") -} - -func associateURL(client *golangsdk.ServiceClient, id string) string { - return client.ServiceURL("qos-specs", id, "associate") -} - -func disassociateURL(client *golangsdk.ServiceClient, id string) string { - return client.ServiceURL("qos-specs", id, "disassociate") -} - -func disassociateAllURL(client *golangsdk.ServiceClient, id string) string { - return client.ServiceURL("qos-specs", id, "disassociate_all") -} - -func listAssociationsURL(client *golangsdk.ServiceClient, id string) string { - return client.ServiceURL("qos-specs", id, "associations") -} diff --git a/openstack/evs/v3/snapshots/requests.go b/openstack/evs/v3/snapshots/requests.go index 6eab5f71e..94a7e761c 100644 --- a/openstack/evs/v3/snapshots/requests.go +++ b/openstack/evs/v3/snapshots/requests.go @@ -37,7 +37,7 @@ func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateRe r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ + resp, err := client.Post(client.ServiceURL("snapshots"), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -46,7 +46,7 @@ func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateRe // Delete will delete the existing Snapshot with the provided ID. func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(client.ServiceURL("snapshots", id), nil) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -54,7 +54,7 @@ func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(client.ServiceURL("snapshots", id), &r.Body, nil) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -107,7 +107,7 @@ func (opts ListOpts) ToSnapshotListQuery() (string, error) { // List returns Snapshots optionally limited by the conditions provided in // ListOpts. func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) + url := client.ServiceURL("snapshots") if opts != nil { query, err := opts.ToSnapshotListQuery() if err != nil { @@ -148,7 +148,7 @@ func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ + resp, err := client.Put(client.ServiceURL("snapshots", id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -183,7 +183,7 @@ func UpdateMetadata(client *golangsdk.ServiceClient, id string, opts UpdateMetad r.Err = err return } - resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ + resp, err := client.Put(client.ServiceURL("snapshots", id, "metadata"), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) diff --git a/openstack/evs/v3/snapshots/urls.go b/openstack/evs/v3/snapshots/urls.go deleted file mode 100644 index 4cd5fee4e..000000000 --- a/openstack/evs/v3/snapshots/urls.go +++ /dev/null @@ -1,31 +0,0 @@ -package snapshots - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("snapshots") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("snapshots", id) -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) -} - -func listURL(c *golangsdk.ServiceClient) string { - return createURL(c) -} - -func updateURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) -} - -func metadataURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("snapshots", id, "metadata") -} - -func updateMetadataURL(c *golangsdk.ServiceClient, id string) string { - return metadataURL(c, id) -} diff --git a/openstack/evs/v3/volumetypes/AddAccess.go b/openstack/evs/v3/volumetypes/AddAccess.go new file mode 100644 index 000000000..bbb1306ec --- /dev/null +++ b/openstack/evs/v3/volumetypes/AddAccess.go @@ -0,0 +1,34 @@ +package volumetypes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// AddAccessOptsBuilder allows extensions to add additional parameters to the +// AddAccess requests. +type AddAccessOptsBuilder interface { + ToVolumeTypeAddAccessMap() (map[string]interface{}, error) +} + +// AddAccessOpts represents options for adding access to a volume type. +type AddAccessOpts struct { + // Project is the project/tenant ID to grant access. + Project string `json:"project"` +} + +// ToVolumeTypeAddAccessMap constructs a request body from AddAccessOpts. +func (opts AddAccessOpts) ToVolumeTypeAddAccessMap() (map[string]interface{}, error) { + return golangsdk.BuildRequestBody(opts, "addProjectAccess") +} + +// AddAccess grants a tenant/project access to a volume type. +func AddAccess(client *golangsdk.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { + b, err := opts.ToVolumeTypeAddAccessMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(client.ServiceURL("types", id, "action"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/Create.go b/openstack/evs/v3/volumetypes/Create.go new file mode 100644 index 000000000..18e118952 --- /dev/null +++ b/openstack/evs/v3/volumetypes/Create.go @@ -0,0 +1,45 @@ +package volumetypes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToVolumeTypeCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for creating a Volume Type. This object is passed to +// the volumetypes.Create function. For more information about these parameters, +// see the Volume Type object. +type CreateOpts struct { + // The name of the volume type + Name string `json:"name" required:"true"` + // The volume type description + Description string `json:"description,omitempty"` + // the ID of the existing volume snapshot + IsPublic *bool `json:"os-volume-type-access:is_public,omitempty"` + // Extra spec key-value pairs defined by the user. + ExtraSpecs map[string]string `json:"extra_specs,omitempty"` +} + +// ToVolumeTypeCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { + return golangsdk.BuildRequestBody(opts, "volume_type") +} + +// Create will create a new Volume Type based on the values in CreateOpts. To extract +// the Volume Type object from the response, call the Extract method on the +// CreateResult. +func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToVolumeTypeCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(client.ServiceURL("types"), b, &r.Body, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/CreateExtraSpecs.go b/openstack/evs/v3/volumetypes/CreateExtraSpecs.go new file mode 100644 index 000000000..8844e600c --- /dev/null +++ b/openstack/evs/v3/volumetypes/CreateExtraSpecs.go @@ -0,0 +1,33 @@ +package volumetypes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the +// CreateExtraSpecs requests. +type CreateExtraSpecsOptsBuilder interface { + ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) +} + +// ExtraSpecsOpts is a map that contains key-value pairs. +type ExtraSpecsOpts map[string]string + +// ToVolumeTypeExtraSpecsCreateMap assembles a body for a Create request based on +// the contents of ExtraSpecsOpts. +func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) { + return map[string]interface{}{"extra_specs": opts}, nil +} + +// CreateExtraSpecs will create or update the extra-specs key-value pairs for +// the specified volume type. +func CreateExtraSpecs(client *golangsdk.ServiceClient, volumeTypeID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { + b, err := opts.ToVolumeTypeExtraSpecsCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(client.ServiceURL("types", volumeTypeID, "extra_specs"), b, &r.Body, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/Delete.go b/openstack/evs/v3/volumetypes/Delete.go new file mode 100644 index 000000000..6ce2752e7 --- /dev/null +++ b/openstack/evs/v3/volumetypes/Delete.go @@ -0,0 +1,10 @@ +package volumetypes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// Delete will delete the existing Volume Type with the provided ID. +func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(client.ServiceURL("types", id), nil) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/DeleteExtraSpec.go b/openstack/evs/v3/volumetypes/DeleteExtraSpec.go new file mode 100644 index 000000000..e609cf4e0 --- /dev/null +++ b/openstack/evs/v3/volumetypes/DeleteExtraSpec.go @@ -0,0 +1,13 @@ +package volumetypes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// DeleteExtraSpec will delete the key-value pair with the given key for the given +// volume type ID. +func DeleteExtraSpec(client *golangsdk.ServiceClient, volumeTypeID, key string) (r DeleteExtraSpecResult) { + resp, err := client.Delete(client.ServiceURL("types", volumeTypeID, "extra_specs", key), &golangsdk.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/Get.go b/openstack/evs/v3/volumetypes/Get.go new file mode 100644 index 000000000..fc47c2523 --- /dev/null +++ b/openstack/evs/v3/volumetypes/Get.go @@ -0,0 +1,11 @@ +package volumetypes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// Get retrieves the Volume Type with the provided ID. To extract the Volume Type object +// from the response, call the Extract method on the GetResult. +func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(client.ServiceURL("types", id), &r.Body, nil) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/GetExtraSpec.go b/openstack/evs/v3/volumetypes/GetExtraSpec.go new file mode 100644 index 000000000..88193f824 --- /dev/null +++ b/openstack/evs/v3/volumetypes/GetExtraSpec.go @@ -0,0 +1,10 @@ +package volumetypes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// GetExtraSpec requests an extra-spec specified by key for the given volume type ID +func GetExtraSpec(client *golangsdk.ServiceClient, volumeTypeID string, key string) (r GetExtraSpecResult) { + resp, err := client.Get(client.ServiceURL("types", volumeTypeID, "extra_specs", key), &r.Body, nil) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/List.go b/openstack/evs/v3/volumetypes/List.go new file mode 100644 index 000000000..8442189ef --- /dev/null +++ b/openstack/evs/v3/volumetypes/List.go @@ -0,0 +1,49 @@ +package volumetypes + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToVolumeTypeListQuery() (string, error) +} + +// ListOpts holds options for listing Volume Types. It is passed to the volumetypes.List +// function. +type ListOpts struct { + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + // Requests a page size of items. + Limit int `q:"limit"` + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToVolumeTypeListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToVolumeTypeListQuery() (string, error) { + q, err := golangsdk.BuildQueryString(opts) + return q.String(), err +} + +// List returns Volume types. +func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := client.ServiceURL("types") + + if opts != nil { + query, err := opts.ToVolumeTypeListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return VolumeTypePage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/evs/v3/volumetypes/ListAccesses.go b/openstack/evs/v3/volumetypes/ListAccesses.go new file mode 100644 index 000000000..0f0783f93 --- /dev/null +++ b/openstack/evs/v3/volumetypes/ListAccesses.go @@ -0,0 +1,15 @@ +package volumetypes + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" +) + +// ListAccesses retrieves the tenants which have access to a volume type. +func ListAccesses(client *golangsdk.ServiceClient, id string) pagination.Pager { + url := client.ServiceURL("types", id, "os-volume-type-access") + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/evs/v3/volumetypes/ListExtraSpecs.go b/openstack/evs/v3/volumetypes/ListExtraSpecs.go new file mode 100644 index 000000000..1cb0e116e --- /dev/null +++ b/openstack/evs/v3/volumetypes/ListExtraSpecs.go @@ -0,0 +1,12 @@ +package volumetypes + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" +) + +// ListExtraSpecs requests all the extra-specs for the given volume type ID. +func ListExtraSpecs(client *golangsdk.ServiceClient, volumeTypeID string) (r ListExtraSpecsResult) { + resp, err := client.Get(client.ServiceURL("types", volumeTypeID, "extra_specs"), &r.Body, nil) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/RemoveAccess.go b/openstack/evs/v3/volumetypes/RemoveAccess.go new file mode 100644 index 000000000..ee524587c --- /dev/null +++ b/openstack/evs/v3/volumetypes/RemoveAccess.go @@ -0,0 +1,34 @@ +package volumetypes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// RemoveAccessOptsBuilder allows extensions to add additional parameters to the +// RemoveAccess requests. +type RemoveAccessOptsBuilder interface { + ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) +} + +// RemoveAccessOpts represents options for removing access to a volume type. +type RemoveAccessOpts struct { + // Project is the project/tenant ID to remove access. + Project string `json:"project"` +} + +// ToVolumeTypeRemoveAccessMap constructs a request body from RemoveAccessOpts. +func (opts RemoveAccessOpts) ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) { + return golangsdk.BuildRequestBody(opts, "removeProjectAccess") +} + +// RemoveAccess removes/revokes a tenant/project access to a volume type. +func RemoveAccess(client *golangsdk.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { + b, err := opts.ToVolumeTypeRemoveAccessMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(client.ServiceURL("types", id, "action"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/Update.go b/openstack/evs/v3/volumetypes/Update.go new file mode 100644 index 000000000..b9cf6ccd4 --- /dev/null +++ b/openstack/evs/v3/volumetypes/Update.go @@ -0,0 +1,39 @@ +package volumetypes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToVolumeTypeUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Volume Type. This object is passed +// to the volumetypes.Update function. For more information about the parameters, see +// the Volume Type object. +type UpdateOpts struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + IsPublic *bool `json:"is_public,omitempty"` +} + +// ToVolumeTypeUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]interface{}, error) { + return golangsdk.BuildRequestBody(opts, "volume_type") +} + +// Update will update the Volume Type with provided information. To extract the updated +// Volume Type from the response, call the Extract method on the UpdateResult. +func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToVolumeTypeUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(client.ServiceURL("types", id), b, &r.Body, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/UpdateExtraSpec.go b/openstack/evs/v3/volumetypes/UpdateExtraSpec.go new file mode 100644 index 000000000..13f7fded9 --- /dev/null +++ b/openstack/evs/v3/volumetypes/UpdateExtraSpec.go @@ -0,0 +1,42 @@ +package volumetypes + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// UpdateExtraSpecOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateExtraSpecOptsBuilder interface { + ToVolumeTypeExtraSpecUpdateMap() (map[string]string, string, error) +} + +// ToVolumeTypeExtraSpecUpdateMap assembles a body for an Update request based on +// the contents of a ExtraSpecOpts. +func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecUpdateMap() (map[string]string, string, error) { + if len(opts) != 1 { + err := golangsdk.ErrInvalidInput{} + err.Argument = "volumetypes.ExtraSpecOpts" + err.Info = "Must have one and only one key-value pair" + return nil, "", err + } + + var key string + for k := range opts { + key = k + } + + return opts, key, nil +} + +// UpdateExtraSpec will updates the value of the specified volume type's extra spec +// for the key in opts. +func UpdateExtraSpec(client *golangsdk.ServiceClient, volumeTypeID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { + b, key, err := opts.ToVolumeTypeExtraSpecUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(client.ServiceURL("types", volumeTypeID, "extra_specs", key), b, &r.Body, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) + return +} diff --git a/openstack/evs/v3/volumetypes/requests.go b/openstack/evs/v3/volumetypes/requests.go deleted file mode 100644 index 5f9c88ca5..000000000 --- a/openstack/evs/v3/volumetypes/requests.go +++ /dev/null @@ -1,306 +0,0 @@ -package volumetypes - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToVolumeTypeCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Volume Type. This object is passed to -// the volumetypes.Create function. For more information about these parameters, -// see the Volume Type object. -type CreateOpts struct { - // The name of the volume type - Name string `json:"name" required:"true"` - // The volume type description - Description string `json:"description,omitempty"` - // the ID of the existing volume snapshot - IsPublic *bool `json:"os-volume-type-access:is_public,omitempty"` - // Extra spec key-value pairs defined by the user. - ExtraSpecs map[string]string `json:"extra_specs,omitempty"` -} - -// ToVolumeTypeCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "volume_type") -} - -// Create will create a new Volume Type based on the values in CreateOpts. To extract -// the Volume Type object from the response, call the Extract method on the -// CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToVolumeTypeCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Delete will delete the existing Volume Type with the provided ID. -func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Get retrieves the Volume Type with the provided ID. To extract the Volume Type object -// from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToVolumeTypeListQuery() (string, error) -} - -// ListOpts holds options for listing Volume Types. It is passed to the volumetypes.List -// function. -type ListOpts struct { - // Comma-separated list of sort keys and optional sort directions in the - // form of [:]. - Sort string `q:"sort"` - // Requests a page size of items. - Limit int `q:"limit"` - // Used in conjunction with limit to return a slice of items. - Offset int `q:"offset"` - // The ID of the last-seen item. - Marker string `q:"marker"` -} - -// ToVolumeTypeListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToVolumeTypeListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// List returns Volume types. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - - if opts != nil { - query, err := opts.ToVolumeTypeListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return VolumeTypePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToVolumeTypeUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contain options for updating an existing Volume Type. This object is passed -// to the volumetypes.Update function. For more information about the parameters, see -// the Volume Type object. -type UpdateOpts struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - IsPublic *bool `json:"is_public,omitempty"` -} - -// ToVolumeTypeUpdateMap assembles a request body based on the contents of an -// UpdateOpts. -func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "volume_type") -} - -// Update will update the Volume Type with provided information. To extract the updated -// Volume Type from the response, call the Extract method on the UpdateResult. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToVolumeTypeUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// ListExtraSpecs requests all the extra-specs for the given volume type ID. -func ListExtraSpecs(client *golangsdk.ServiceClient, volumeTypeID string) (r ListExtraSpecsResult) { - resp, err := client.Get(extraSpecsListURL(client, volumeTypeID), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// GetExtraSpec requests an extra-spec specified by key for the given volume type ID -func GetExtraSpec(client *golangsdk.ServiceClient, volumeTypeID string, key string) (r GetExtraSpecResult) { - resp, err := client.Get(extraSpecsGetURL(client, volumeTypeID, key), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the -// CreateExtraSpecs requests. -type CreateExtraSpecsOptsBuilder interface { - ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) -} - -// ExtraSpecsOpts is a map that contains key-value pairs. -type ExtraSpecsOpts map[string]string - -// ToVolumeTypeExtraSpecsCreateMap assembles a body for a Create request based on -// the contents of ExtraSpecsOpts. -func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) { - return map[string]interface{}{"extra_specs": opts}, nil -} - -// CreateExtraSpecs will create or update the extra-specs key-value pairs for -// the specified volume type. -func CreateExtraSpecs(client *golangsdk.ServiceClient, volumeTypeID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { - b, err := opts.ToVolumeTypeExtraSpecsCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(extraSpecsCreateURL(client, volumeTypeID), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// UpdateExtraSpecOptsBuilder allows extensions to add additional parameters to -// the Update request. -type UpdateExtraSpecOptsBuilder interface { - ToVolumeTypeExtraSpecUpdateMap() (map[string]string, string, error) -} - -// ToVolumeTypeExtraSpecUpdateMap assembles a body for an Update request based on -// the contents of a ExtraSpecOpts. -func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecUpdateMap() (map[string]string, string, error) { - if len(opts) != 1 { - err := golangsdk.ErrInvalidInput{} - err.Argument = "volumetypes.ExtraSpecOpts" - err.Info = "Must have one and only one key-value pair" - return nil, "", err - } - - var key string - for k := range opts { - key = k - } - - return opts, key, nil -} - -// UpdateExtraSpec will updates the value of the specified volume type's extra spec -// for the key in opts. -func UpdateExtraSpec(client *golangsdk.ServiceClient, volumeTypeID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { - b, key, err := opts.ToVolumeTypeExtraSpecUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(extraSpecUpdateURL(client, volumeTypeID, key), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// DeleteExtraSpec will delete the key-value pair with the given key for the given -// volume type ID. -func DeleteExtraSpec(client *golangsdk.ServiceClient, volumeTypeID, key string) (r DeleteExtraSpecResult) { - resp, err := client.Delete(extraSpecDeleteURL(client, volumeTypeID, key), &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// ListAccesses retrieves the tenants which have access to a volume type. -func ListAccesses(client *golangsdk.ServiceClient, id string) pagination.Pager { - url := accessURL(client, id) - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return AccessPage{pagination.SinglePageBase(r)} - }) -} - -// AddAccessOptsBuilder allows extensions to add additional parameters to the -// AddAccess requests. -type AddAccessOptsBuilder interface { - ToVolumeTypeAddAccessMap() (map[string]interface{}, error) -} - -// AddAccessOpts represents options for adding access to a volume type. -type AddAccessOpts struct { - // Project is the project/tenant ID to grant access. - Project string `json:"project"` -} - -// ToVolumeTypeAddAccessMap constructs a request body from AddAccessOpts. -func (opts AddAccessOpts) ToVolumeTypeAddAccessMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "addProjectAccess") -} - -// AddAccess grants a tenant/project access to a volume type. -func AddAccess(client *golangsdk.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { - b, err := opts.ToVolumeTypeAddAccessMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(accessActionURL(client, id), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// RemoveAccessOptsBuilder allows extensions to add additional parameters to the -// RemoveAccess requests. -type RemoveAccessOptsBuilder interface { - ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) -} - -// RemoveAccessOpts represents options for removing access to a volume type. -type RemoveAccessOpts struct { - // Project is the project/tenant ID to remove access. - Project string `json:"project"` -} - -// ToVolumeTypeRemoveAccessMap constructs a request body from RemoveAccessOpts. -func (opts RemoveAccessOpts) ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "removeProjectAccess") -} - -// RemoveAccess removes/revokes a tenant/project access to a volume type. -func RemoveAccess(client *golangsdk.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { - b, err := opts.ToVolumeTypeRemoveAccessMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(accessActionURL(client, id), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumetypes/urls.go b/openstack/evs/v3/volumetypes/urls.go deleted file mode 100644 index ee92ffd6b..000000000 --- a/openstack/evs/v3/volumetypes/urls.go +++ /dev/null @@ -1,51 +0,0 @@ -package volumetypes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("types") -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("types", id) -} - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("types") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("types", id) -} - -func updateURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("types", id) -} - -func extraSpecsListURL(client *golangsdk.ServiceClient, id string) string { - return client.ServiceURL("types", id, "extra_specs") -} - -func extraSpecsGetURL(client *golangsdk.ServiceClient, id, key string) string { - return client.ServiceURL("types", id, "extra_specs", key) -} - -func extraSpecsCreateURL(client *golangsdk.ServiceClient, id string) string { - return client.ServiceURL("types", id, "extra_specs") -} - -func extraSpecUpdateURL(client *golangsdk.ServiceClient, id, key string) string { - return client.ServiceURL("types", id, "extra_specs", key) -} - -func extraSpecDeleteURL(client *golangsdk.ServiceClient, id, key string) string { - return client.ServiceURL("types", id, "extra_specs", key) -} - -func accessURL(client *golangsdk.ServiceClient, id string) string { - return client.ServiceURL("types", id, "os-volume-type-access") -} - -func accessActionURL(client *golangsdk.ServiceClient, id string) string { - return client.ServiceURL("types", id, "action") -} From 09a6f66d1620ca2d4dd92c8972f845ff74ce26fe Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 13:13:18 +0100 Subject: [PATCH 13/51] List --- openstack/evs/v3/volumes/List.go | 2 +- openstack/evs/v3/volumetypes/List.go | 53 ++++++++++++++++--------- openstack/evs/v3/volumetypes/results.go | 34 +++++----------- 3 files changed, 45 insertions(+), 44 deletions(-) diff --git a/openstack/evs/v3/volumes/List.go b/openstack/evs/v3/volumes/List.go index 9ce0a12df..dfcc636af 100644 --- a/openstack/evs/v3/volumes/List.go +++ b/openstack/evs/v3/volumes/List.go @@ -74,5 +74,5 @@ func ExtractVolumes(r pagination.Page) ([]Volume, error) { // ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes func ExtractVolumesInto(r pagination.Page, v interface{}) error { - return extract.IntoStructPtr(r.(VolumePage).Result.BodyReader(), v, "volumes") + return extract.IntoSlicePtr(r.(VolumePage).Result.BodyReader(), v, "volumes") } diff --git a/openstack/evs/v3/volumetypes/List.go b/openstack/evs/v3/volumetypes/List.go index 8442189ef..fe43952e2 100644 --- a/openstack/evs/v3/volumetypes/List.go +++ b/openstack/evs/v3/volumetypes/List.go @@ -2,17 +2,11 @@ package volumetypes import ( "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToVolumeTypeListQuery() (string, error) -} - -// ListOpts holds options for listing Volume Types. It is passed to the volumetypes.List -// function. +// ListOpts holds options for listing Volume Types. It is passed to the volumetypes.List function. type ListOpts struct { // Comma-separated list of sort keys and optional sort directions in the // form of [:]. @@ -32,18 +26,41 @@ func (opts ListOpts) ToVolumeTypeListQuery() (string, error) { } // List returns Volume types. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := client.ServiceURL("types") - - if opts != nil { - query, err := opts.ToVolumeTypeListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query +func List(client *golangsdk.ServiceClient, opts ListOpts) pagination.Pager { + q, err := golangsdk.BuildQueryString(opts) + if err != nil { + return pagination.Pager{Err: err} } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(client, client.ServiceURL("types")+q.String(), func(r pagination.PageResult) pagination.Page { return VolumeTypePage{pagination.LinkedPageBase{PageResult: r}} }) } + +// VolumeTypePage is a pagination.pager that is returned from a call to the List function. +type VolumeTypePage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a ListResult contains no Volume Types. +func (r VolumeTypePage) IsEmpty() (bool, error) { + volumeTypes, err := ExtractVolumeTypes(r) + return len(volumeTypes) == 0, err +} + +func (r VolumeTypePage) NextPageURL() (string, error) { + var s struct { + Links []golangsdk.Link `json:"volume_type_links"` + } + + err := extract.Into(r.BodyReader(), &s) + if err != nil { + return "", err + } + return golangsdk.ExtractNextURL(s.Links) +} + +// ExtractVolumeTypesInto similar to ExtractInto but operates on a `list` of volume types +func ExtractVolumeTypesInto(r pagination.Page, v interface{}) error { + return extract.IntoSlicePtr(r.(VolumeTypePage).BodyReader(), v, "volume_types") +} diff --git a/openstack/evs/v3/volumetypes/results.go b/openstack/evs/v3/volumetypes/results.go index e58873407..ad0919d72 100644 --- a/openstack/evs/v3/volumetypes/results.go +++ b/openstack/evs/v3/volumetypes/results.go @@ -23,26 +23,15 @@ type VolumeType struct { PublicAccess bool `json:"os-volume-type-access:is_public"` } -// VolumeTypePage is a pagination.pager that is returned from a call to the List function. -type VolumeTypePage struct { - pagination.LinkedPageBase -} - -// IsEmpty returns true if a ListResult contains no Volume Types. -func (r VolumeTypePage) IsEmpty() (bool, error) { - volumetypes, err := ExtractVolumeTypes(r) - return len(volumetypes) == 0, err -} - -func (page VolumeTypePage) NextPageURL() (string, error) { - var s struct { - Links []golangsdk.Link `json:"volume_type_links"` - } - err := page.ExtractInto(&s) - if err != nil { - return "", err - } - return golangsdk.ExtractNextURL(s.Links) +type ExtraSpecs struct { + // Reserved field + VolumeBackendName string `json:"volume_backend_name"` + // Reserved field + AvailabilityZone string `json:"availability-zone"` + // Reserved field + HWAZ string `json:"HW:availability_zone"` + // Specifies the AZs that support the current disk type. + RESKEYAZ string `json:"RESKEY:availability_zones"` } // ExtractVolumeTypes extracts and returns Volumes. It is used while iterating over a volumetypes.List call. @@ -68,11 +57,6 @@ func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "volume_type") } -// ExtractVolumeTypesInto similar to ExtractInto but operates on a `list` of volume types -func ExtractVolumeTypesInto(r pagination.Page, v interface{}) error { - return r.(VolumeTypePage).Result.ExtractIntoSlicePtr(v, "volume_types") -} - // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult From d20e07fc8407d923ae4444386064d23caf546ce0 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 13:23:51 +0100 Subject: [PATCH 14/51] volumetypes --- openstack/evs/v3/attachments/doc.go | 86 --------- openstack/evs/v3/qos/doc.go | 146 -------------- openstack/evs/v3/snapshots/doc.go | 61 ------ openstack/evs/v3/volumes/doc.go | 23 --- openstack/evs/v3/volumetypes/AddAccess.go | 34 ---- openstack/evs/v3/volumetypes/Create.go | 45 ----- .../evs/v3/volumetypes/CreateExtraSpecs.go | 33 ---- openstack/evs/v3/volumetypes/Delete.go | 10 - .../evs/v3/volumetypes/DeleteExtraSpec.go | 13 -- openstack/evs/v3/volumetypes/Get.go | 52 ++++- openstack/evs/v3/volumetypes/GetExtraSpec.go | 10 - openstack/evs/v3/volumetypes/ListAccesses.go | 15 -- .../evs/v3/volumetypes/ListExtraSpecs.go | 12 -- openstack/evs/v3/volumetypes/RemoveAccess.go | 34 ---- openstack/evs/v3/volumetypes/Update.go | 39 ---- .../evs/v3/volumetypes/UpdateExtraSpec.go | 42 ----- openstack/evs/v3/volumetypes/doc.go | 164 ---------------- openstack/evs/v3/volumetypes/results.go | 178 ------------------ 18 files changed, 44 insertions(+), 953 deletions(-) delete mode 100644 openstack/evs/v3/attachments/doc.go delete mode 100644 openstack/evs/v3/qos/doc.go delete mode 100644 openstack/evs/v3/snapshots/doc.go delete mode 100644 openstack/evs/v3/volumes/doc.go delete mode 100644 openstack/evs/v3/volumetypes/AddAccess.go delete mode 100644 openstack/evs/v3/volumetypes/Create.go delete mode 100644 openstack/evs/v3/volumetypes/CreateExtraSpecs.go delete mode 100644 openstack/evs/v3/volumetypes/Delete.go delete mode 100644 openstack/evs/v3/volumetypes/DeleteExtraSpec.go delete mode 100644 openstack/evs/v3/volumetypes/GetExtraSpec.go delete mode 100644 openstack/evs/v3/volumetypes/ListAccesses.go delete mode 100644 openstack/evs/v3/volumetypes/ListExtraSpecs.go delete mode 100644 openstack/evs/v3/volumetypes/RemoveAccess.go delete mode 100644 openstack/evs/v3/volumetypes/Update.go delete mode 100644 openstack/evs/v3/volumetypes/UpdateExtraSpec.go delete mode 100644 openstack/evs/v3/volumetypes/doc.go delete mode 100644 openstack/evs/v3/volumetypes/results.go diff --git a/openstack/evs/v3/attachments/doc.go b/openstack/evs/v3/attachments/doc.go deleted file mode 100644 index 838d8962f..000000000 --- a/openstack/evs/v3/attachments/doc.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -Package attachments provides access to OpenStack Block Storage Attachment -API's. Use of this package requires Cinder version 3.27 at a minimum. - -For more information, see: -https://docs.openstack.org/api-ref/block-storage/v3/index.html#attachments - -Example to List Attachments - - listOpts := &attachments.ListOpts{ - InstanceID: "uuid", - } - - client.Microversion = "3.27" - allPages, err := attachments.List(client, listOpts).AllPages() - if err != nil { - panic(err) - } - - allAttachments, err := attachments.ExtractAttachments(allPages) - if err != nil { - panic(err) - } - - for _, attachment := range allAttachments { - fmt.Println(attachment) - } - -Example to Create Attachment - - createOpts := &attachments.CreateOpts{ - InstanceUUID: "uuid", - VolumeUUID: "uuid" - } - - client.Microversion = "3.27" - attachment, err := attachments.Create(client, createOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Println(attachment) - -Example to Get Attachment - - client.Microversion = "3.27" - attachment, err := attachments.Get(client, "uuid").Extract() - if err != nil { - panic(err) - } - - fmt.Println(attachment) - -Example to Update Attachment - - opts := &attachments.UpdateOpts{ - Connector: map[string]interface{}{ - "mode": "ro", - } - } - - client.Microversion = "3.27" - attachment, err := attachments.Update(client, "uuid", opts).Extract() - if err != nil { - panic(err) - } - - fmt.Println(attachment) - -Example to Complete Attachment - - client.Microversion = "3.44" - err := attachments.Complete(client, "uuid").ExtractErr() - if err != nil { - panic(err) - } - -Example to Delete Attachment - - client.Microversion = "3.27" - err := attachments.Delete(client, "uuid").ExtractErr() - if err != nil { - panic(err) - } -*/ -package attachments diff --git a/openstack/evs/v3/qos/doc.go b/openstack/evs/v3/qos/doc.go deleted file mode 100644 index 86f288fa5..000000000 --- a/openstack/evs/v3/qos/doc.go +++ /dev/null @@ -1,146 +0,0 @@ -/* -Package qos provides information and interaction with the QoS specifications -for the Openstack Blockstorage service. - -Example to create a QoS specification - - createOpts := qos.CreateOpts{ - Name: "test", - Consumer: qos.ConsumerFront, - Specs: map[string]string{ - "read_iops_sec": "20000", - }, - } - - test, err := qos.Create(client, createOpts).Extract() - if err != nil { - log.Fatal(err) - } - - fmt.Printf("QoS: %+v\n", test) - -Example to delete a QoS specification - - qosID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" - - deleteOpts := qos.DeleteOpts{ - Force: false, - } - - err = qos.Delete(client, qosID, deleteOpts).ExtractErr() - if err != nil { - log.Fatal(err) - } - -Example to list QoS specifications - - listOpts := qos.ListOpts{} - - allPages, err := qos.List(client, listOpts).AllPages() - if err != nil { - panic(err) - } - - allQoS, err := qos.ExtractQoS(allPages) - if err != nil { - panic(err) - } - - for _, qos := range allQoS { - fmt.Printf("List: %+v\n", qos) - } - -Example to get a single QoS specification - - qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" - - singleQos, err := qos.Get(client, test.ID).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("Get: %+v\n", singleQos) - -Example of updating QoSSpec - - qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" - - updateOpts := qos.UpdateOpts{ - Consumer: qos.ConsumerBack, - Specs: map[string]string{ - "read_iops_sec": "40000", - }, - } - - specs, err := qos.Update(client, qosID, updateOpts).Extract() - if err != nil { - panic(err) - } - fmt.Printf("%+v\n", specs) - -Example of deleting specific keys/specs from a QoS - - qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" - - keysToDelete := qos.DeleteKeysOpts{"read_iops_sec"} - err = qos.DeleteKeys(client, qosID, keysToDelete).ExtractErr() - if err != nil { - panic(err) - } - -Example of associating a QoS with a volume type - - qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" - volID := "b596be6a-0ce9-43fa-804a-5c5e181ede76" - - associateOpts := qos.AssociateOpts{ - VolumeTypeID: volID, - } - - err = qos.Associate(client, qosID, associateOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example of disassociating a QoS from a volume type - - qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" - volID := "b596be6a-0ce9-43fa-804a-5c5e181ede76" - - disassociateOpts := qos.DisassociateOpts{ - VolumeTypeID: volID, - } - - err = qos.Disassociate(client, qosID, disassociateOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example of disaassociating a Qos from all volume types - - qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" - - err = qos.DisassociateAll(client, qosID).ExtractErr() - if err != nil { - panic(err) - } - -Example of listing all associations of a QoS - - qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" - - allQosAssociations, err := qos.ListAssociations(client, qosID).AllPages() - if err != nil { - panic(err) - } - - allAssociations, err := qos.ExtractAssociations(allQosAssociations) - if err != nil { - panic(err) - } - - for _, association := range allAssociations { - fmt.Printf("Association: %+v\n", association) - } -*/ -package qos diff --git a/openstack/evs/v3/snapshots/doc.go b/openstack/evs/v3/snapshots/doc.go deleted file mode 100644 index 702094c3d..000000000 --- a/openstack/evs/v3/snapshots/doc.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Package snapshots provides information and interaction with snapshots in the -OpenStack Block Storage service. A snapshot is a point in time copy of the -data contained in an external storage volume, and can be controlled -programmatically. - -Example to list Snapshots - - allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() - if err != nil{ - panic(err) - } - snapshots, err := snapshots.ExtractSnapshots(allPages) - if err != nil{ - panic(err) - } - for _,s := range snapshots{ - fmt.Println(s) - } - -Example to get a Snapshot - - snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" - snapshot, err := snapshots.Get(client, snapshotID).Extract() - if err != nil{ - panic(err) - } - fmt.Println(snapshot) - -Example to create a Snapshot - - snapshot, err := snapshots.Create(client, snapshots.CreateOpts{ - Name:"snapshot_001", - VolumeID:"5aa119a8-d25b-45a7-8d1b-88e127885635", - }).Extract() - if err != nil{ - panic(err) - } - fmt.Println(snapshot) - -Example to delete a Snapshot - - snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" - err := snapshots.Delete(client, snapshotID).ExtractErr() - if err != nil{ - panic(err) - } - -Example to update a Snapshot - - snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" - snapshot, err = snapshots.Update(client, snapshotID, snapshots.UpdateOpts{ - Name: "snapshot_002", - Description:"description_002", - }).Extract() - if err != nil{ - panic(err) - } - fmt.Println(snapshot) -*/ -package snapshots diff --git a/openstack/evs/v3/volumes/doc.go b/openstack/evs/v3/volumes/doc.go deleted file mode 100644 index 0b834852d..000000000 --- a/openstack/evs/v3/volumes/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Package volumes provides information and interaction with volumes in the -OpenStack Block Storage service. A volume is a detachable block storage -device, akin to a USB hard drive. It can only be attached to one instance at -a time. - -Example to create a Volume from a Backup - - backupID := "20c792f0-bb03-434f-b653-06ef238e337e" - options := volumes.CreateOpts{ - Name: "vol-001", - BackupID: &backupID, - } - - client.Microversion = "3.47" - volume, err := volumes.Create(client, options).Extract() - if err != nil { - panic(err) - } - - fmt.Println(volume) -*/ -package volumes diff --git a/openstack/evs/v3/volumetypes/AddAccess.go b/openstack/evs/v3/volumetypes/AddAccess.go deleted file mode 100644 index bbb1306ec..000000000 --- a/openstack/evs/v3/volumetypes/AddAccess.go +++ /dev/null @@ -1,34 +0,0 @@ -package volumetypes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -// AddAccessOptsBuilder allows extensions to add additional parameters to the -// AddAccess requests. -type AddAccessOptsBuilder interface { - ToVolumeTypeAddAccessMap() (map[string]interface{}, error) -} - -// AddAccessOpts represents options for adding access to a volume type. -type AddAccessOpts struct { - // Project is the project/tenant ID to grant access. - Project string `json:"project"` -} - -// ToVolumeTypeAddAccessMap constructs a request body from AddAccessOpts. -func (opts AddAccessOpts) ToVolumeTypeAddAccessMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "addProjectAccess") -} - -// AddAccess grants a tenant/project access to a volume type. -func AddAccess(client *golangsdk.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { - b, err := opts.ToVolumeTypeAddAccessMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("types", id, "action"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumetypes/Create.go b/openstack/evs/v3/volumetypes/Create.go deleted file mode 100644 index 18e118952..000000000 --- a/openstack/evs/v3/volumetypes/Create.go +++ /dev/null @@ -1,45 +0,0 @@ -package volumetypes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToVolumeTypeCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Volume Type. This object is passed to -// the volumetypes.Create function. For more information about these parameters, -// see the Volume Type object. -type CreateOpts struct { - // The name of the volume type - Name string `json:"name" required:"true"` - // The volume type description - Description string `json:"description,omitempty"` - // the ID of the existing volume snapshot - IsPublic *bool `json:"os-volume-type-access:is_public,omitempty"` - // Extra spec key-value pairs defined by the user. - ExtraSpecs map[string]string `json:"extra_specs,omitempty"` -} - -// ToVolumeTypeCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "volume_type") -} - -// Create will create a new Volume Type based on the values in CreateOpts. To extract -// the Volume Type object from the response, call the Extract method on the -// CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToVolumeTypeCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("types"), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumetypes/CreateExtraSpecs.go b/openstack/evs/v3/volumetypes/CreateExtraSpecs.go deleted file mode 100644 index 8844e600c..000000000 --- a/openstack/evs/v3/volumetypes/CreateExtraSpecs.go +++ /dev/null @@ -1,33 +0,0 @@ -package volumetypes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -// CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the -// CreateExtraSpecs requests. -type CreateExtraSpecsOptsBuilder interface { - ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) -} - -// ExtraSpecsOpts is a map that contains key-value pairs. -type ExtraSpecsOpts map[string]string - -// ToVolumeTypeExtraSpecsCreateMap assembles a body for a Create request based on -// the contents of ExtraSpecsOpts. -func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) { - return map[string]interface{}{"extra_specs": opts}, nil -} - -// CreateExtraSpecs will create or update the extra-specs key-value pairs for -// the specified volume type. -func CreateExtraSpecs(client *golangsdk.ServiceClient, volumeTypeID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { - b, err := opts.ToVolumeTypeExtraSpecsCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("types", volumeTypeID, "extra_specs"), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumetypes/Delete.go b/openstack/evs/v3/volumetypes/Delete.go deleted file mode 100644 index 6ce2752e7..000000000 --- a/openstack/evs/v3/volumetypes/Delete.go +++ /dev/null @@ -1,10 +0,0 @@ -package volumetypes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -// Delete will delete the existing Volume Type with the provided ID. -func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(client.ServiceURL("types", id), nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumetypes/DeleteExtraSpec.go b/openstack/evs/v3/volumetypes/DeleteExtraSpec.go deleted file mode 100644 index e609cf4e0..000000000 --- a/openstack/evs/v3/volumetypes/DeleteExtraSpec.go +++ /dev/null @@ -1,13 +0,0 @@ -package volumetypes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -// DeleteExtraSpec will delete the key-value pair with the given key for the given -// volume type ID. -func DeleteExtraSpec(client *golangsdk.ServiceClient, volumeTypeID, key string) (r DeleteExtraSpecResult) { - resp, err := client.Delete(client.ServiceURL("types", volumeTypeID, "extra_specs", key), &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumetypes/Get.go b/openstack/evs/v3/volumetypes/Get.go index fc47c2523..bf8611e90 100644 --- a/openstack/evs/v3/volumetypes/Get.go +++ b/openstack/evs/v3/volumetypes/Get.go @@ -1,11 +1,47 @@ package volumetypes -import "github.com/opentelekomcloud/gophertelekomcloud" - -// Get retrieves the Volume Type with the provided ID. To extract the Volume Type object -// from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(client.ServiceURL("types", id), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +// Get retrieves the Volume Type with the provided ID. +func Get(client *golangsdk.ServiceClient, id string) (*VolumeType, error) { + raw, err := client.Get(client.ServiceURL("types", id), nil, nil) + if err != nil { + return nil, err + } + + var res VolumeType + err = extract.Into(raw.Body, &res) + return &res, err +} + +// VolumeType contains all the information associated with an OpenStack Volume Type. +type VolumeType struct { + // Unique identifier for the volume type. + ID string `json:"id"` + // Human-readable display name for the volume type. + Name string `json:"name"` + // Human-readable description for the volume type. + Description string `json:"description"` + // Arbitrary key-value pairs defined by the user. + ExtraSpecs map[string]string `json:"extra_specs"` + // Whether the volume type is publicly visible. + IsPublic bool `json:"is_public"` + // Qos Spec ID + QosSpecID string `json:"qos_specs_id"` + // Volume Type access public attribute + PublicAccess bool `json:"os-volume-type-access:is_public"` +} + +type ExtraSpecs struct { + // Reserved field + VolumeBackendName string `json:"volume_backend_name"` + // Reserved field + AvailabilityZone string `json:"availability-zone"` + // Reserved field + HWAZ string `json:"HW:availability_zone"` + // Specifies the AZs that support the current disk type. + RESKEYAZ string `json:"RESKEY:availability_zones"` } diff --git a/openstack/evs/v3/volumetypes/GetExtraSpec.go b/openstack/evs/v3/volumetypes/GetExtraSpec.go deleted file mode 100644 index 88193f824..000000000 --- a/openstack/evs/v3/volumetypes/GetExtraSpec.go +++ /dev/null @@ -1,10 +0,0 @@ -package volumetypes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -// GetExtraSpec requests an extra-spec specified by key for the given volume type ID -func GetExtraSpec(client *golangsdk.ServiceClient, volumeTypeID string, key string) (r GetExtraSpecResult) { - resp, err := client.Get(client.ServiceURL("types", volumeTypeID, "extra_specs", key), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumetypes/ListAccesses.go b/openstack/evs/v3/volumetypes/ListAccesses.go deleted file mode 100644 index 0f0783f93..000000000 --- a/openstack/evs/v3/volumetypes/ListAccesses.go +++ /dev/null @@ -1,15 +0,0 @@ -package volumetypes - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// ListAccesses retrieves the tenants which have access to a volume type. -func ListAccesses(client *golangsdk.ServiceClient, id string) pagination.Pager { - url := client.ServiceURL("types", id, "os-volume-type-access") - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return AccessPage{pagination.SinglePageBase(r)} - }) -} diff --git a/openstack/evs/v3/volumetypes/ListExtraSpecs.go b/openstack/evs/v3/volumetypes/ListExtraSpecs.go deleted file mode 100644 index 1cb0e116e..000000000 --- a/openstack/evs/v3/volumetypes/ListExtraSpecs.go +++ /dev/null @@ -1,12 +0,0 @@ -package volumetypes - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// ListExtraSpecs requests all the extra-specs for the given volume type ID. -func ListExtraSpecs(client *golangsdk.ServiceClient, volumeTypeID string) (r ListExtraSpecsResult) { - resp, err := client.Get(client.ServiceURL("types", volumeTypeID, "extra_specs"), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumetypes/RemoveAccess.go b/openstack/evs/v3/volumetypes/RemoveAccess.go deleted file mode 100644 index ee524587c..000000000 --- a/openstack/evs/v3/volumetypes/RemoveAccess.go +++ /dev/null @@ -1,34 +0,0 @@ -package volumetypes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -// RemoveAccessOptsBuilder allows extensions to add additional parameters to the -// RemoveAccess requests. -type RemoveAccessOptsBuilder interface { - ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) -} - -// RemoveAccessOpts represents options for removing access to a volume type. -type RemoveAccessOpts struct { - // Project is the project/tenant ID to remove access. - Project string `json:"project"` -} - -// ToVolumeTypeRemoveAccessMap constructs a request body from RemoveAccessOpts. -func (opts RemoveAccessOpts) ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "removeProjectAccess") -} - -// RemoveAccess removes/revokes a tenant/project access to a volume type. -func RemoveAccess(client *golangsdk.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { - b, err := opts.ToVolumeTypeRemoveAccessMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("types", id, "action"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumetypes/Update.go b/openstack/evs/v3/volumetypes/Update.go deleted file mode 100644 index b9cf6ccd4..000000000 --- a/openstack/evs/v3/volumetypes/Update.go +++ /dev/null @@ -1,39 +0,0 @@ -package volumetypes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToVolumeTypeUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contain options for updating an existing Volume Type. This object is passed -// to the volumetypes.Update function. For more information about the parameters, see -// the Volume Type object. -type UpdateOpts struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - IsPublic *bool `json:"is_public,omitempty"` -} - -// ToVolumeTypeUpdateMap assembles a request body based on the contents of an -// UpdateOpts. -func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "volume_type") -} - -// Update will update the Volume Type with provided information. To extract the updated -// Volume Type from the response, call the Extract method on the UpdateResult. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToVolumeTypeUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(client.ServiceURL("types", id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumetypes/UpdateExtraSpec.go b/openstack/evs/v3/volumetypes/UpdateExtraSpec.go deleted file mode 100644 index 13f7fded9..000000000 --- a/openstack/evs/v3/volumetypes/UpdateExtraSpec.go +++ /dev/null @@ -1,42 +0,0 @@ -package volumetypes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -// UpdateExtraSpecOptsBuilder allows extensions to add additional parameters to -// the Update request. -type UpdateExtraSpecOptsBuilder interface { - ToVolumeTypeExtraSpecUpdateMap() (map[string]string, string, error) -} - -// ToVolumeTypeExtraSpecUpdateMap assembles a body for an Update request based on -// the contents of a ExtraSpecOpts. -func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecUpdateMap() (map[string]string, string, error) { - if len(opts) != 1 { - err := golangsdk.ErrInvalidInput{} - err.Argument = "volumetypes.ExtraSpecOpts" - err.Info = "Must have one and only one key-value pair" - return nil, "", err - } - - var key string - for k := range opts { - key = k - } - - return opts, key, nil -} - -// UpdateExtraSpec will updates the value of the specified volume type's extra spec -// for the key in opts. -func UpdateExtraSpec(client *golangsdk.ServiceClient, volumeTypeID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { - b, key, err := opts.ToVolumeTypeExtraSpecUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(client.ServiceURL("types", volumeTypeID, "extra_specs", key), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/volumetypes/doc.go b/openstack/evs/v3/volumetypes/doc.go deleted file mode 100644 index 55a2170bc..000000000 --- a/openstack/evs/v3/volumetypes/doc.go +++ /dev/null @@ -1,164 +0,0 @@ -/* -Package volumetypes provides information and interaction with volume types in the -OpenStack Block Storage service. A volume type is a collection of specs used to -define the volume capabilities. - -Example to list Volume Types - - allPages, err := volumetypes.List(client, volumetypes.ListOpts{}).AllPages() - if err != nil{ - panic(err) - } - volumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) - if err != nil{ - panic(err) - } - for _,vt := range volumeTypes{ - fmt.Println(vt) - } - -Example to show a Volume Type - - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - volumeType, err := volumetypes.Get(client, typeID).Extract() - if err != nil{ - panic(err) - } - fmt.Println(volumeType) - -Example to create a Volume Type - - volumeType, err := volumetypes.Create(client, volumetypes.CreateOpts{ - Name:"volume_type_001", - IsPublic:true, - Description:"description_001", - }).Extract() - if err != nil{ - panic(err) - } - fmt.Println(volumeType) - -Example to delete a Volume Type - - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - err := volumetypes.Delete(client, typeID).ExtractErr() - if err != nil{ - panic(err) - } - -Example to update a Volume Type - - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - volumetype, err = volumetypes.Update(client, typeID, volumetypes.UpdateOpts{ - Name: "volume_type_002", - Description:"description_002", - IsPublic:false, - }).Extract() - if err != nil{ - panic(err) - } - fmt.Println(volumetype) - -Example to Create Extra Specs for a Volume Type - - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - - createOpts := volumetypes.ExtraSpecsOpts{ - "capabilities": "gpu", - } - createdExtraSpecs, err := volumetypes.CreateExtraSpecs(client, typeID, createOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v", createdExtraSpecs) - -Example to Get Extra Specs for a Volume Type - - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - - extraSpecs, err := volumetypes.ListExtraSpecs(client, typeID).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v", extraSpecs) - -Example to Get specific Extra Spec for a Volume Type - - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - - extraSpec, err := volumetypes.GetExtraSpec(client, typeID, "capabilities").Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v", extraSpec) - -Example to Update Extra Specs for a Volume Type - - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - - updateOpts := volumetypes.ExtraSpecsOpts{ - "capabilities": "capabilities-updated", - } - updatedExtraSpec, err := volumetypes.UpdateExtraSpec(client, typeID, updateOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v", updatedExtraSpec) - -Example to Delete an Extra Spec for a Volume Type - - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - err := volumetypes.DeleteExtraSpec(client, typeID, "capabilities").ExtractErr() - if err != nil { - panic(err) - } - -Example to List Volume Type Access - - typeID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" - - allPages, err := volumetypes.ListAccesses(client, typeID).AllPages() - if err != nil { - panic(err) - } - - allAccesses, err := volumetypes.ExtractAccesses(allPages) - if err != nil { - panic(err) - } - - for _, access := range allAccesses { - fmt.Printf("%+v", access) - } - -Example to Grant Access to a Volume Type - - typeID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" - - accessOpts := volumetypes.AddAccessOpts{ - Project: "15153a0979884b59b0592248ef947921", - } - - err := volumetypes.AddAccess(client, typeID, accessOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example to Remove/Revoke Access to a Volume Type - - typeID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" - - accessOpts := volumetypes.RemoveAccessOpts{ - Project: "15153a0979884b59b0592248ef947921", - } - - err := volumetypes.RemoveAccess(client, typeID, accessOpts).ExtractErr() - if err != nil { - panic(err) - } -*/ -package volumetypes diff --git a/openstack/evs/v3/volumetypes/results.go b/openstack/evs/v3/volumetypes/results.go deleted file mode 100644 index ad0919d72..000000000 --- a/openstack/evs/v3/volumetypes/results.go +++ /dev/null @@ -1,178 +0,0 @@ -package volumetypes - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// VolumeType contains all the information associated with an OpenStack Volume Type. -type VolumeType struct { - // Unique identifier for the volume type. - ID string `json:"id"` - // Human-readable display name for the volume type. - Name string `json:"name"` - // Human-readable description for the volume type. - Description string `json:"description"` - // Arbitrary key-value pairs defined by the user. - ExtraSpecs map[string]string `json:"extra_specs"` - // Whether the volume type is publicly visible. - IsPublic bool `json:"is_public"` - // Qos Spec ID - QosSpecID string `json:"qos_specs_id"` - // Volume Type access public attribute - PublicAccess bool `json:"os-volume-type-access:is_public"` -} - -type ExtraSpecs struct { - // Reserved field - VolumeBackendName string `json:"volume_backend_name"` - // Reserved field - AvailabilityZone string `json:"availability-zone"` - // Reserved field - HWAZ string `json:"HW:availability_zone"` - // Specifies the AZs that support the current disk type. - RESKEYAZ string `json:"RESKEY:availability_zones"` -} - -// ExtractVolumeTypes extracts and returns Volumes. It is used while iterating over a volumetypes.List call. -func ExtractVolumeTypes(r pagination.Page) ([]VolumeType, error) { - var s []VolumeType - err := ExtractVolumeTypesInto(r, &s) - return s, err -} - -type commonResult struct { - golangsdk.Result -} - -// Extract will get the Volume Type object out of the commonResult object. -func (r commonResult) Extract() (*VolumeType, error) { - var s VolumeType - err := r.ExtractInto(&s) - return &s, err -} - -// ExtractInto converts our response data into a volume type struct -func (r commonResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "volume_type") -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - golangsdk.ErrResult -} - -// UpdateResult contains the response body and error from an Update request. -type UpdateResult struct { - commonResult -} - -// extraSpecsResult contains the result of a call for (potentially) multiple -// key-value pairs. Call its Extract method to interpret it as a -// map[string]interface. -type extraSpecsResult struct { - golangsdk.Result -} - -// ListExtraSpecsResult contains the result of a Get operation. Call its Extract -// method to interpret it as a map[string]interface. -type ListExtraSpecsResult struct { - extraSpecsResult -} - -// CreateExtraSpecsResult contains the result of a Create operation. Call its -// Extract method to interpret it as a map[string]interface. -type CreateExtraSpecsResult struct { - extraSpecsResult -} - -// Extract interprets any extraSpecsResult as ExtraSpecs, if possible. -func (r extraSpecsResult) Extract() (map[string]string, error) { - var s struct { - ExtraSpecs map[string]string `json:"extra_specs"` - } - err := r.ExtractInto(&s) - return s.ExtraSpecs, err -} - -// extraSpecResult contains the result of a call for individual a single -// key-value pair. -type extraSpecResult struct { - golangsdk.Result -} - -// GetExtraSpecResult contains the result of a Get operation. Call its Extract -// method to interpret it as a map[string]interface. -type GetExtraSpecResult struct { - extraSpecResult -} - -// UpdateExtraSpecResult contains the result of an Update operation. Call its -// Extract method to interpret it as a map[string]interface. -type UpdateExtraSpecResult struct { - extraSpecResult -} - -// DeleteExtraSpecResult contains the result of a Delete operation. Call its -// ExtractErr method to determine if the call succeeded or failed. -type DeleteExtraSpecResult struct { - golangsdk.ErrResult -} - -// Extract interprets any extraSpecResult as an ExtraSpec, if possible. -func (r extraSpecResult) Extract() (map[string]string, error) { - var s map[string]string - err := r.ExtractInto(&s) - return s, err -} - -// VolumeTypeAccess represents an ACL of project access to a specific Volume Type. -type VolumeTypeAccess struct { - // VolumeTypeID is the unique ID of the volume type. - VolumeTypeID string `json:"volume_type_id"` - - // ProjectID is the unique ID of the project. - ProjectID string `json:"project_id"` -} - -// AccessPage contains a single page of all VolumeTypeAccess entries for a volume type. -type AccessPage struct { - pagination.SinglePageBase -} - -// IsEmpty indicates whether an AccessPage is empty. -func (page AccessPage) IsEmpty() (bool, error) { - v, err := ExtractAccesses(page) - return len(v) == 0, err -} - -// ExtractAccesses interprets a page of results as a slice of VolumeTypeAccess. -func ExtractAccesses(r pagination.Page) ([]VolumeTypeAccess, error) { - var s struct { - VolumeTypeAccesses []VolumeTypeAccess `json:"volume_type_access"` - } - err := (r.(AccessPage)).ExtractInto(&s) - return s.VolumeTypeAccesses, err -} - -// AddAccessResult is the response from a AddAccess request. Call its -// ExtractErr method to determine if the request succeeded or failed. -type AddAccessResult struct { - golangsdk.ErrResult -} - -// RemoveAccessResult is the response from a RemoveAccess request. Call its -// ExtractErr method to determine if the request succeeded or failed. -type RemoveAccessResult struct { - golangsdk.ErrResult -} From a67046a45995ef6ebec1ce953bd053a9135db28a Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 13:26:24 +0100 Subject: [PATCH 15/51] move --- .../openstack/evs/extensions/backups_test.go | 8 +-- .../openstack/evs/extensions/extensions.go | 61 ++++++++++--------- .../openstack/evs/extensions/limits_test.go | 3 +- .../evs/extensions/schedulerhints_test.go | 3 +- .../evs/extensions/schedulerstats_test.go | 9 +-- .../openstack/evs/extensions/services_test.go | 7 ++- .../evs/extensions/volumetenants_test.go | 3 +- acceptance/openstack/evs/v3/quotaset_test.go | 37 +++++------ .../extensions/availabilityzones/doc.go | 0 .../extensions/availabilityzones/requests.go | 0 .../extensions/availabilityzones/results.go | 0 .../extensions/availabilityzones/urls.go | 0 .../evs/{ => v3}/extensions/backups/doc.go | 0 .../{ => v3}/extensions/backups/requests.go | 0 .../{ => v3}/extensions/backups/results.go | 0 .../evs/{ => v3}/extensions/backups/urls.go | 0 .../evs/{ => v3}/extensions/limits/doc.go | 0 .../{ => v3}/extensions/limits/requests.go | 0 .../evs/{ => v3}/extensions/limits/results.go | 0 .../evs/{ => v3}/extensions/limits/urls.go | 0 .../evs/{ => v3}/extensions/quotasets/doc.go | 0 .../{ => v3}/extensions/quotasets/requests.go | 0 .../{ => v3}/extensions/quotasets/results.go | 0 .../evs/{ => v3}/extensions/quotasets/urls.go | 0 .../{ => v3}/extensions/schedulerhints/doc.go | 0 .../extensions/schedulerhints/requests.go | 0 .../{ => v3}/extensions/schedulerstats/doc.go | 0 .../extensions/schedulerstats/requests.go | 0 .../extensions/schedulerstats/results.go | 0 .../extensions/schedulerstats/urls.go | 0 .../evs/{ => v3}/extensions/services/doc.go | 0 .../{ => v3}/extensions/services/requests.go | 0 .../{ => v3}/extensions/services/results.go | 0 .../evs/{ => v3}/extensions/services/urls.go | 0 .../{ => v3}/extensions/volumeactions/doc.go | 0 .../extensions/volumeactions/requests.go | 0 .../extensions/volumeactions/results.go | 0 .../{ => v3}/extensions/volumeactions/urls.go | 0 .../evs/{ => v3}/extensions/volumehost/doc.go | 0 .../{ => v3}/extensions/volumehost/results.go | 0 .../{ => v3}/extensions/volumetenants/doc.go | 0 .../extensions/volumetenants/results.go | 0 .../extensions/volumetransfers/doc.go | 0 .../extensions/volumetransfers/requests.go | 0 .../extensions/volumetransfers/results.go | 0 .../extensions/volumetransfers/urls.go | 0 46 files changed, 69 insertions(+), 62 deletions(-) rename openstack/evs/{ => v3}/extensions/availabilityzones/doc.go (100%) rename openstack/evs/{ => v3}/extensions/availabilityzones/requests.go (100%) rename openstack/evs/{ => v3}/extensions/availabilityzones/results.go (100%) rename openstack/evs/{ => v3}/extensions/availabilityzones/urls.go (100%) rename openstack/evs/{ => v3}/extensions/backups/doc.go (100%) rename openstack/evs/{ => v3}/extensions/backups/requests.go (100%) rename openstack/evs/{ => v3}/extensions/backups/results.go (100%) rename openstack/evs/{ => v3}/extensions/backups/urls.go (100%) rename openstack/evs/{ => v3}/extensions/limits/doc.go (100%) rename openstack/evs/{ => v3}/extensions/limits/requests.go (100%) rename openstack/evs/{ => v3}/extensions/limits/results.go (100%) rename openstack/evs/{ => v3}/extensions/limits/urls.go (100%) rename openstack/evs/{ => v3}/extensions/quotasets/doc.go (100%) rename openstack/evs/{ => v3}/extensions/quotasets/requests.go (100%) rename openstack/evs/{ => v3}/extensions/quotasets/results.go (100%) rename openstack/evs/{ => v3}/extensions/quotasets/urls.go (100%) rename openstack/evs/{ => v3}/extensions/schedulerhints/doc.go (100%) rename openstack/evs/{ => v3}/extensions/schedulerhints/requests.go (100%) rename openstack/evs/{ => v3}/extensions/schedulerstats/doc.go (100%) rename openstack/evs/{ => v3}/extensions/schedulerstats/requests.go (100%) rename openstack/evs/{ => v3}/extensions/schedulerstats/results.go (100%) rename openstack/evs/{ => v3}/extensions/schedulerstats/urls.go (100%) rename openstack/evs/{ => v3}/extensions/services/doc.go (100%) rename openstack/evs/{ => v3}/extensions/services/requests.go (100%) rename openstack/evs/{ => v3}/extensions/services/results.go (100%) rename openstack/evs/{ => v3}/extensions/services/urls.go (100%) rename openstack/evs/{ => v3}/extensions/volumeactions/doc.go (100%) rename openstack/evs/{ => v3}/extensions/volumeactions/requests.go (100%) rename openstack/evs/{ => v3}/extensions/volumeactions/results.go (100%) rename openstack/evs/{ => v3}/extensions/volumeactions/urls.go (100%) rename openstack/evs/{ => v3}/extensions/volumehost/doc.go (100%) rename openstack/evs/{ => v3}/extensions/volumehost/results.go (100%) rename openstack/evs/{ => v3}/extensions/volumetenants/doc.go (100%) rename openstack/evs/{ => v3}/extensions/volumetenants/results.go (100%) rename openstack/evs/{ => v3}/extensions/volumetransfers/doc.go (100%) rename openstack/evs/{ => v3}/extensions/volumetransfers/requests.go (100%) rename openstack/evs/{ => v3}/extensions/volumetransfers/results.go (100%) rename openstack/evs/{ => v3}/extensions/volumetransfers/urls.go (100%) diff --git a/acceptance/openstack/evs/extensions/backups_test.go b/acceptance/openstack/evs/extensions/backups_test.go index d4a5d94ec..ad8b433f5 100644 --- a/acceptance/openstack/evs/extensions/backups_test.go +++ b/acceptance/openstack/evs/extensions/backups_test.go @@ -3,9 +3,9 @@ package extensions import ( "testing" - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/backups" + backups2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/backups" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" blockstorage "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/evs/v3" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) @@ -22,10 +22,10 @@ func TestBackupsCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteBackup(t, blockClient, backup.ID) - allPages, err := backups.List(blockClient, nil).AllPages() + allPages, err := backups2.List(blockClient, nil).AllPages() th.AssertNoErr(t, err) - allBackups, err := backups.ExtractBackups(allPages) + allBackups, err := backups2.ExtractBackups(allPages) th.AssertNoErr(t, err) var found bool diff --git a/acceptance/openstack/evs/extensions/extensions.go b/acceptance/openstack/evs/extensions/extensions.go index f7b0abbef..90d621407 100644 --- a/acceptance/openstack/evs/extensions/extensions.go +++ b/acceptance/openstack/evs/extensions/extensions.go @@ -8,12 +8,13 @@ import ( "strings" "testing" + backups2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/backups" + volumeactions2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/volumeactions" + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/images" "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/servers" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/backups" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/volumeactions" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v2/volumes" v3 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumetypes" @@ -22,18 +23,18 @@ import ( // CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be // returned -func CreateUploadImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) (volumeactions.VolumeImage, error) { +func CreateUploadImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) (volumeactions2.VolumeImage, error) { if testing.Short() { t.Skip("Skipping test that requires volume-backed image uploading in short mode.") } imageName := tools.RandomString("ACPTTEST", 16) - uploadImageOpts := volumeactions.UploadImageOpts{ + uploadImageOpts := volumeactions2.UploadImageOpts{ ImageName: imageName, Force: true, } - volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts) + volumeImage, err := volumeactions2.UploadImage(client, volume.ID, uploadImageOpts) if err != nil { return volumeImage, err } @@ -74,7 +75,7 @@ func CreateVolumeAttach(t *testing.T, client *golangsdk.ServiceClient, volume *v t.Skip("Skipping test that requires volume attachment in short mode.") } - attachOpts := volumeactions.AttachOpts{ + attachOpts := volumeactions2.AttachOpts{ MountPoint: "/mnt", Mode: "rw", InstanceUUID: server.ID, @@ -82,7 +83,7 @@ func CreateVolumeAttach(t *testing.T, client *golangsdk.ServiceClient, volume *v t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) - if err := volumeactions.Attach(client, volume.ID, attachOpts); err != nil { + if err := volumeactions2.Attach(client, volume.ID, attachOpts); err != nil { return err } @@ -104,7 +105,7 @@ func CreateVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume * t.Logf("Attempting to reserve volume %s", volume.ID) - if err := volumeactions.Reserve(client, volume.ID); err != nil { + if err := volumeactions2.Reserve(client, volume.ID); err != nil { return err } @@ -119,11 +120,11 @@ func CreateVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume * func DeleteVolumeAttach(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) { t.Logf("Attepting to detach volume volume: %s", volume.ID) - detachOpts := volumeactions.DetachOpts{ + detachOpts := volumeactions2.DetachOpts{ AttachmentID: volume.Attachments[0].AttachmentID, } - if err := volumeactions.Detach(client, volume.ID, detachOpts); err != nil { + if err := volumeactions2.Detach(client, volume.ID, detachOpts); err != nil { t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) } @@ -144,7 +145,7 @@ func DeleteVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume * t.Logf("Attempting to unreserve volume %s", volume.ID) - if err := volumeactions.Unreserve(client, volume.ID); err != nil { + if err := volumeactions2.Unreserve(client, volume.ID); err != nil { t.Fatalf("Unable to unreserve volume %s: %v", volume.ID, err) } @@ -155,11 +156,11 @@ func DeleteVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume * func ExtendVolumeSize(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { t.Logf("Attempting to extend the size of volume %s", volume.ID) - extendOpts := volumeactions.ExtendSizeOpts{ + extendOpts := volumeactions2.ExtendSizeOpts{ NewSize: 2, } - err := volumeactions.ExtendSize(client, volume.ID, extendOpts) + err := volumeactions2.ExtendSize(client, volume.ID, extendOpts) if err != nil { return err } @@ -175,13 +176,13 @@ func ExtendVolumeSize(t *testing.T, client *golangsdk.ServiceClient, volume *vol func SetImageMetadata(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { t.Logf("Attempting to apply image metadata to volume %s", volume.ID) - imageMetadataOpts := volumeactions.ImageMetadataOpts{ + imageMetadataOpts := volumeactions2.ImageMetadataOpts{ Metadata: map[string]string{ "image_name": "testimage", }, } - err := volumeactions.SetImageMetadata(client, volume.ID, imageMetadataOpts) + err := volumeactions2.SetImageMetadata(client, volume.ID, imageMetadataOpts) if err != nil { return err } @@ -191,16 +192,16 @@ func SetImageMetadata(t *testing.T, client *golangsdk.ServiceClient, volume *vol // CreateBackup will create a backup based on a volume. An error will be // will be returned if the backup could not be created. -func CreateBackup(t *testing.T, client *golangsdk.ServiceClient, volumeID string) (*backups.Backup, error) { +func CreateBackup(t *testing.T, client *golangsdk.ServiceClient, volumeID string) (*backups2.Backup, error) { t.Logf("Attempting to create a backup of volume %s", volumeID) backupName := tools.RandomString("ACPTTEST", 16) - createOpts := backups.CreateOpts{ + createOpts := backups2.CreateOpts{ VolumeID: volumeID, Name: backupName, } - backup, err := backups.Create(client, createOpts) + backup, err := backups2.Create(client, createOpts) if err != nil { return nil, err } @@ -210,7 +211,7 @@ func CreateBackup(t *testing.T, client *golangsdk.ServiceClient, volumeID string return nil, err } - backup, err = backups.Get(client, backup.ID) + backup, err = backups2.Get(client, backup.ID) if err != nil { return nil, err } @@ -226,7 +227,7 @@ func CreateBackup(t *testing.T, client *golangsdk.ServiceClient, volumeID string // DeleteBackup will delete a backup. A fatal error will occur if the backup // could not be deleted. This works best when used as a deferred function. func DeleteBackup(t *testing.T, client *golangsdk.ServiceClient, backupID string) { - if err := backups.Delete(client, backupID); err != nil { + if err := backups2.Delete(client, backupID); err != nil { t.Fatalf("Unable to delete backup %s: %s", backupID, err) } @@ -237,7 +238,7 @@ func DeleteBackup(t *testing.T, client *golangsdk.ServiceClient, backupID string // status. It will do this for the amount of seconds defined. func WaitForBackupStatus(client *golangsdk.ServiceClient, id, status string) error { return tools.WaitFor(func() (bool, error) { - current, err := backups.Get(client, id) + current, err := backups2.Get(client, id) if err != nil { return false, err } @@ -254,11 +255,11 @@ func WaitForBackupStatus(client *golangsdk.ServiceClient, id, status string) err func SetBootable(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { t.Logf("Attempting to apply bootable status to volume %s", volume.ID) - bootableOpts := volumeactions.BootableOpts{ + bootableOpts := volumeactions2.BootableOpts{ Bootable: true, } - err := volumeactions.SetBootable(client, volume.ID, bootableOpts) + err := volumeactions2.SetBootable(client, volume.ID, bootableOpts) if err != nil { return err } @@ -272,11 +273,11 @@ func SetBootable(t *testing.T, client *golangsdk.ServiceClient, volume *volumes. return fmt.Errorf("Volume bootable status is %q, expected 'true'", vol.Bootable) } - bootableOpts = volumeactions.BootableOpts{ + bootableOpts = volumeactions2.BootableOpts{ Bootable: false, } - err = volumeactions.SetBootable(client, volume.ID, bootableOpts) + err = volumeactions2.SetBootable(client, volume.ID, bootableOpts) if err != nil { return err } @@ -297,12 +298,12 @@ func SetBootable(t *testing.T, client *golangsdk.ServiceClient, volume *volumes. func ChangeVolumeType(t *testing.T, client *golangsdk.ServiceClient, volume *v3.Volume, vt *volumetypes.VolumeType) error { t.Logf("Attempting to change the type of volume %s from %s to %s", volume.ID, volume.VolumeType, vt.Name) - changeOpts := volumeactions.ChangeTypeOpts{ + changeOpts := volumeactions2.ChangeTypeOpts{ NewType: vt.Name, - MigrationPolicy: volumeactions.MigrationPolicyOnDemand, + MigrationPolicy: volumeactions2.MigrationPolicyOnDemand, } - err := volumeactions.ChangeType(client, volume.ID, changeOpts) + err := volumeactions2.ChangeType(client, volume.ID, changeOpts) if err != nil { return err } @@ -318,12 +319,12 @@ func ChangeVolumeType(t *testing.T, client *golangsdk.ServiceClient, volume *v3. func ReImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume, imageID string) error { t.Logf("Attempting to re-image volume %s", volume.ID) - reimageOpts := volumeactions.ReImageOpts{ + reimageOpts := volumeactions2.ReImageOpts{ ImageID: imageID, ReImageReserved: false, } - err := volumeactions.ReImage(client, volume.ID, reimageOpts) + err := volumeactions2.ReImage(client, volume.ID, reimageOpts) if err != nil { return err } diff --git a/acceptance/openstack/evs/extensions/limits_test.go b/acceptance/openstack/evs/extensions/limits_test.go index 9cadaedc6..a0b837625 100644 --- a/acceptance/openstack/evs/extensions/limits_test.go +++ b/acceptance/openstack/evs/extensions/limits_test.go @@ -3,9 +3,10 @@ package extensions import ( "testing" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/limits" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/limits" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) diff --git a/acceptance/openstack/evs/extensions/schedulerhints_test.go b/acceptance/openstack/evs/extensions/schedulerhints_test.go index d407d1788..ff646b2b2 100644 --- a/acceptance/openstack/evs/extensions/schedulerhints_test.go +++ b/acceptance/openstack/evs/extensions/schedulerhints_test.go @@ -3,9 +3,10 @@ package extensions import ( "testing" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/schedulerhints" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/schedulerhints" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) diff --git a/acceptance/openstack/evs/extensions/schedulerstats_test.go b/acceptance/openstack/evs/extensions/schedulerstats_test.go index 174906c5f..96a6fd16a 100644 --- a/acceptance/openstack/evs/extensions/schedulerstats_test.go +++ b/acceptance/openstack/evs/extensions/schedulerstats_test.go @@ -3,9 +3,10 @@ package extensions import ( "testing" + schedulerstats2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/schedulerstats" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/schedulerstats" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) @@ -15,14 +16,14 @@ func TestSchedulerStatsList(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - listOpts := schedulerstats.ListOpts{ + listOpts := schedulerstats2.ListOpts{ Detail: true, } - allPages, err := schedulerstats.List(blockClient, listOpts).AllPages() + allPages, err := schedulerstats2.List(blockClient, listOpts).AllPages() th.AssertNoErr(t, err) - allStats, err := schedulerstats.ExtractStoragePools(allPages) + allStats, err := schedulerstats2.ExtractStoragePools(allPages) th.AssertNoErr(t, err) for _, stat := range allStats { diff --git a/acceptance/openstack/evs/extensions/services_test.go b/acceptance/openstack/evs/extensions/services_test.go index 7d7b08a5a..5cbe7ad53 100644 --- a/acceptance/openstack/evs/extensions/services_test.go +++ b/acceptance/openstack/evs/extensions/services_test.go @@ -3,9 +3,10 @@ package extensions import ( "testing" + services2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/services" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/services" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) @@ -15,10 +16,10 @@ func TestServicesList(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - allPages, err := services.List(blockClient, services.ListOpts{}).AllPages() + allPages, err := services2.List(blockClient, services2.ListOpts{}).AllPages() th.AssertNoErr(t, err) - allServices, err := services.ExtractServices(allPages) + allServices, err := services2.ExtractServices(allPages) th.AssertNoErr(t, err) for _, service := range allServices { diff --git a/acceptance/openstack/evs/extensions/volumetenants_test.go b/acceptance/openstack/evs/extensions/volumetenants_test.go index 6cd011461..639588dcb 100644 --- a/acceptance/openstack/evs/extensions/volumetenants_test.go +++ b/acceptance/openstack/evs/extensions/volumetenants_test.go @@ -3,9 +3,10 @@ package extensions import ( "testing" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/volumetenants" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" blockstorage "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/evs/v3" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/volumetenants" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) diff --git a/acceptance/openstack/evs/v3/quotaset_test.go b/acceptance/openstack/evs/v3/quotaset_test.go index 6eb13d659..0041defc6 100644 --- a/acceptance/openstack/evs/v3/quotaset_test.go +++ b/acceptance/openstack/evs/v3/quotaset_test.go @@ -4,10 +4,11 @@ import ( "os" "testing" + quotasets2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/quotasets" + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/quotasets" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumetypes" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) @@ -17,7 +18,7 @@ func TestQuotasetGet(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSet, err := quotasets.Get(client, projectID) + quotaSet, err := quotasets2.Get(client, projectID) th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) @@ -28,7 +29,7 @@ func TestQuotasetGetDefaults(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSet, err := quotasets.GetDefaults(client, projectID) + quotaSet, err := quotasets2.GetDefaults(client, projectID) th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) @@ -39,13 +40,13 @@ func TestQuotasetGetUsage(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSetUsage, err := quotasets.GetUsage(client, projectID) + quotaSetUsage, err := quotasets2.GetUsage(client, projectID) th.AssertNoErr(t, err) tools.PrintResource(t, quotaSetUsage) } -var UpdateQuotaOpts = quotasets.UpdateOpts{ +var UpdateQuotaOpts = quotasets2.UpdateOpts{ Volumes: golangsdk.IntToPointer(100), Snapshots: golangsdk.IntToPointer(200), Gigabytes: golangsdk.IntToPointer(300), @@ -58,7 +59,7 @@ var UpdateQuotaOpts = quotasets.UpdateOpts{ }, } -var UpdatedQuotas = quotasets.QuotaSet{ +var UpdatedQuotas = quotasets2.QuotaSet{ Volumes: 100, Snapshots: 200, Gigabytes: 300, @@ -82,7 +83,7 @@ func TestQuotasetUpdate(t *testing.T) { client, projectID := getClientAndProject(t) // save original quotas - orig, err := quotasets.Get(client, projectID) + orig, err := quotasets2.Get(client, projectID) th.AssertNoErr(t, err) // create volumeType to test volume type quota @@ -90,24 +91,24 @@ func TestQuotasetUpdate(t *testing.T) { th.AssertNoErr(t, err) defer func() { - restore := quotasets.UpdateOpts{} + restore := quotasets2.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) err := volumetypes.Delete(client, volumeType.ID) th.AssertNoErr(t, err) - _, err = quotasets.Update(client, projectID, restore) + _, err = quotasets2.Update(client, projectID, restore) th.AssertNoErr(t, err) }() // test Update - resultQuotas, err := quotasets.Update(client, projectID, UpdateQuotaOpts) + resultQuotas, err := quotasets2.Update(client, projectID, UpdateQuotaOpts) th.AssertNoErr(t, err) // We dont know the default quotas, so just check if the quotas are not the // same as before - newQuotas, err := quotasets.Get(client, projectID) + newQuotas, err := quotasets2.Get(client, projectID) th.AssertNoErr(t, err) th.AssertEquals(t, resultQuotas.Volumes, newQuotas.Volumes) th.AssertEquals(t, resultQuotas.Extra["volumes_foo"], newQuotas.Extra["volumes_foo"]) @@ -140,26 +141,26 @@ func TestQuotasetDelete(t *testing.T) { client, projectID := getClientAndProject(t) // save original quotas - orig, err := quotasets.Get(client, projectID) + orig, err := quotasets2.Get(client, projectID) th.AssertNoErr(t, err) defer func() { - restore := quotasets.UpdateOpts{} + restore := quotasets2.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) - _, err = quotasets.Update(client, projectID, restore) + _, err = quotasets2.Update(client, projectID, restore) th.AssertNoErr(t, err) }() // Obtain environment default quotaset values to validate deletion. - defaultQuotaSet, err := quotasets.GetDefaults(client, projectID) + defaultQuotaSet, err := quotasets2.GetDefaults(client, projectID) th.AssertNoErr(t, err) // Test Delete - err = quotasets.Delete(client, projectID) + err = quotasets2.Delete(client, projectID) th.AssertNoErr(t, err) - newQuotas, err := quotasets.Get(client, projectID) + newQuotas, err := quotasets2.Get(client, projectID) th.AssertNoErr(t, err) th.AssertEquals(t, newQuotas.Volumes, defaultQuotaSet.Volumes) @@ -176,7 +177,7 @@ func getClientAndProject(t *testing.T) (*golangsdk.ServiceClient, string) { return client, projectID } -func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) { +func FillUpdateOptsFromQuotaSet(src quotasets2.QuotaSet, dest *quotasets2.UpdateOpts) { dest.Volumes = &src.Volumes dest.Snapshots = &src.Snapshots dest.Gigabytes = &src.Gigabytes diff --git a/openstack/evs/extensions/availabilityzones/doc.go b/openstack/evs/v3/extensions/availabilityzones/doc.go similarity index 100% rename from openstack/evs/extensions/availabilityzones/doc.go rename to openstack/evs/v3/extensions/availabilityzones/doc.go diff --git a/openstack/evs/extensions/availabilityzones/requests.go b/openstack/evs/v3/extensions/availabilityzones/requests.go similarity index 100% rename from openstack/evs/extensions/availabilityzones/requests.go rename to openstack/evs/v3/extensions/availabilityzones/requests.go diff --git a/openstack/evs/extensions/availabilityzones/results.go b/openstack/evs/v3/extensions/availabilityzones/results.go similarity index 100% rename from openstack/evs/extensions/availabilityzones/results.go rename to openstack/evs/v3/extensions/availabilityzones/results.go diff --git a/openstack/evs/extensions/availabilityzones/urls.go b/openstack/evs/v3/extensions/availabilityzones/urls.go similarity index 100% rename from openstack/evs/extensions/availabilityzones/urls.go rename to openstack/evs/v3/extensions/availabilityzones/urls.go diff --git a/openstack/evs/extensions/backups/doc.go b/openstack/evs/v3/extensions/backups/doc.go similarity index 100% rename from openstack/evs/extensions/backups/doc.go rename to openstack/evs/v3/extensions/backups/doc.go diff --git a/openstack/evs/extensions/backups/requests.go b/openstack/evs/v3/extensions/backups/requests.go similarity index 100% rename from openstack/evs/extensions/backups/requests.go rename to openstack/evs/v3/extensions/backups/requests.go diff --git a/openstack/evs/extensions/backups/results.go b/openstack/evs/v3/extensions/backups/results.go similarity index 100% rename from openstack/evs/extensions/backups/results.go rename to openstack/evs/v3/extensions/backups/results.go diff --git a/openstack/evs/extensions/backups/urls.go b/openstack/evs/v3/extensions/backups/urls.go similarity index 100% rename from openstack/evs/extensions/backups/urls.go rename to openstack/evs/v3/extensions/backups/urls.go diff --git a/openstack/evs/extensions/limits/doc.go b/openstack/evs/v3/extensions/limits/doc.go similarity index 100% rename from openstack/evs/extensions/limits/doc.go rename to openstack/evs/v3/extensions/limits/doc.go diff --git a/openstack/evs/extensions/limits/requests.go b/openstack/evs/v3/extensions/limits/requests.go similarity index 100% rename from openstack/evs/extensions/limits/requests.go rename to openstack/evs/v3/extensions/limits/requests.go diff --git a/openstack/evs/extensions/limits/results.go b/openstack/evs/v3/extensions/limits/results.go similarity index 100% rename from openstack/evs/extensions/limits/results.go rename to openstack/evs/v3/extensions/limits/results.go diff --git a/openstack/evs/extensions/limits/urls.go b/openstack/evs/v3/extensions/limits/urls.go similarity index 100% rename from openstack/evs/extensions/limits/urls.go rename to openstack/evs/v3/extensions/limits/urls.go diff --git a/openstack/evs/extensions/quotasets/doc.go b/openstack/evs/v3/extensions/quotasets/doc.go similarity index 100% rename from openstack/evs/extensions/quotasets/doc.go rename to openstack/evs/v3/extensions/quotasets/doc.go diff --git a/openstack/evs/extensions/quotasets/requests.go b/openstack/evs/v3/extensions/quotasets/requests.go similarity index 100% rename from openstack/evs/extensions/quotasets/requests.go rename to openstack/evs/v3/extensions/quotasets/requests.go diff --git a/openstack/evs/extensions/quotasets/results.go b/openstack/evs/v3/extensions/quotasets/results.go similarity index 100% rename from openstack/evs/extensions/quotasets/results.go rename to openstack/evs/v3/extensions/quotasets/results.go diff --git a/openstack/evs/extensions/quotasets/urls.go b/openstack/evs/v3/extensions/quotasets/urls.go similarity index 100% rename from openstack/evs/extensions/quotasets/urls.go rename to openstack/evs/v3/extensions/quotasets/urls.go diff --git a/openstack/evs/extensions/schedulerhints/doc.go b/openstack/evs/v3/extensions/schedulerhints/doc.go similarity index 100% rename from openstack/evs/extensions/schedulerhints/doc.go rename to openstack/evs/v3/extensions/schedulerhints/doc.go diff --git a/openstack/evs/extensions/schedulerhints/requests.go b/openstack/evs/v3/extensions/schedulerhints/requests.go similarity index 100% rename from openstack/evs/extensions/schedulerhints/requests.go rename to openstack/evs/v3/extensions/schedulerhints/requests.go diff --git a/openstack/evs/extensions/schedulerstats/doc.go b/openstack/evs/v3/extensions/schedulerstats/doc.go similarity index 100% rename from openstack/evs/extensions/schedulerstats/doc.go rename to openstack/evs/v3/extensions/schedulerstats/doc.go diff --git a/openstack/evs/extensions/schedulerstats/requests.go b/openstack/evs/v3/extensions/schedulerstats/requests.go similarity index 100% rename from openstack/evs/extensions/schedulerstats/requests.go rename to openstack/evs/v3/extensions/schedulerstats/requests.go diff --git a/openstack/evs/extensions/schedulerstats/results.go b/openstack/evs/v3/extensions/schedulerstats/results.go similarity index 100% rename from openstack/evs/extensions/schedulerstats/results.go rename to openstack/evs/v3/extensions/schedulerstats/results.go diff --git a/openstack/evs/extensions/schedulerstats/urls.go b/openstack/evs/v3/extensions/schedulerstats/urls.go similarity index 100% rename from openstack/evs/extensions/schedulerstats/urls.go rename to openstack/evs/v3/extensions/schedulerstats/urls.go diff --git a/openstack/evs/extensions/services/doc.go b/openstack/evs/v3/extensions/services/doc.go similarity index 100% rename from openstack/evs/extensions/services/doc.go rename to openstack/evs/v3/extensions/services/doc.go diff --git a/openstack/evs/extensions/services/requests.go b/openstack/evs/v3/extensions/services/requests.go similarity index 100% rename from openstack/evs/extensions/services/requests.go rename to openstack/evs/v3/extensions/services/requests.go diff --git a/openstack/evs/extensions/services/results.go b/openstack/evs/v3/extensions/services/results.go similarity index 100% rename from openstack/evs/extensions/services/results.go rename to openstack/evs/v3/extensions/services/results.go diff --git a/openstack/evs/extensions/services/urls.go b/openstack/evs/v3/extensions/services/urls.go similarity index 100% rename from openstack/evs/extensions/services/urls.go rename to openstack/evs/v3/extensions/services/urls.go diff --git a/openstack/evs/extensions/volumeactions/doc.go b/openstack/evs/v3/extensions/volumeactions/doc.go similarity index 100% rename from openstack/evs/extensions/volumeactions/doc.go rename to openstack/evs/v3/extensions/volumeactions/doc.go diff --git a/openstack/evs/extensions/volumeactions/requests.go b/openstack/evs/v3/extensions/volumeactions/requests.go similarity index 100% rename from openstack/evs/extensions/volumeactions/requests.go rename to openstack/evs/v3/extensions/volumeactions/requests.go diff --git a/openstack/evs/extensions/volumeactions/results.go b/openstack/evs/v3/extensions/volumeactions/results.go similarity index 100% rename from openstack/evs/extensions/volumeactions/results.go rename to openstack/evs/v3/extensions/volumeactions/results.go diff --git a/openstack/evs/extensions/volumeactions/urls.go b/openstack/evs/v3/extensions/volumeactions/urls.go similarity index 100% rename from openstack/evs/extensions/volumeactions/urls.go rename to openstack/evs/v3/extensions/volumeactions/urls.go diff --git a/openstack/evs/extensions/volumehost/doc.go b/openstack/evs/v3/extensions/volumehost/doc.go similarity index 100% rename from openstack/evs/extensions/volumehost/doc.go rename to openstack/evs/v3/extensions/volumehost/doc.go diff --git a/openstack/evs/extensions/volumehost/results.go b/openstack/evs/v3/extensions/volumehost/results.go similarity index 100% rename from openstack/evs/extensions/volumehost/results.go rename to openstack/evs/v3/extensions/volumehost/results.go diff --git a/openstack/evs/extensions/volumetenants/doc.go b/openstack/evs/v3/extensions/volumetenants/doc.go similarity index 100% rename from openstack/evs/extensions/volumetenants/doc.go rename to openstack/evs/v3/extensions/volumetenants/doc.go diff --git a/openstack/evs/extensions/volumetenants/results.go b/openstack/evs/v3/extensions/volumetenants/results.go similarity index 100% rename from openstack/evs/extensions/volumetenants/results.go rename to openstack/evs/v3/extensions/volumetenants/results.go diff --git a/openstack/evs/extensions/volumetransfers/doc.go b/openstack/evs/v3/extensions/volumetransfers/doc.go similarity index 100% rename from openstack/evs/extensions/volumetransfers/doc.go rename to openstack/evs/v3/extensions/volumetransfers/doc.go diff --git a/openstack/evs/extensions/volumetransfers/requests.go b/openstack/evs/v3/extensions/volumetransfers/requests.go similarity index 100% rename from openstack/evs/extensions/volumetransfers/requests.go rename to openstack/evs/v3/extensions/volumetransfers/requests.go diff --git a/openstack/evs/extensions/volumetransfers/results.go b/openstack/evs/v3/extensions/volumetransfers/results.go similarity index 100% rename from openstack/evs/extensions/volumetransfers/results.go rename to openstack/evs/v3/extensions/volumetransfers/results.go diff --git a/openstack/evs/extensions/volumetransfers/urls.go b/openstack/evs/v3/extensions/volumetransfers/urls.go similarity index 100% rename from openstack/evs/extensions/volumetransfers/urls.go rename to openstack/evs/v3/extensions/volumetransfers/urls.go From c4c676af3b2deeabd60ede35353b5cf09ffea89c Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 13:32:17 +0100 Subject: [PATCH 16/51] reverse --- openstack/evs/extensions/quotasets/delete.go | 10 + openstack/evs/extensions/quotasets/get.go | 50 +++ .../evs/extensions/quotasets/get_usage.go | 65 ++++ .../extensions/quotasets/testing/fixtures.go | 163 ++++++++++ .../quotasets/testing/requests_test.go | 63 ++++ openstack/evs/extensions/quotasets/update.go | 47 +++ .../evs/extensions/schedulerstats/list.go | 96 ++++++ .../schedulerstats/testing/fixtures.go | 107 +++++++ .../schedulerstats/testing/requests_test.go | 25 ++ openstack/evs/extensions/services/list.go | 75 +++++ .../extensions/services/testing/fixtures.go | 98 ++++++ .../services/testing/requests_test.go | 26 ++ .../evs/extensions/volumeactions/attach.go | 31 ++ .../volumeactions/begin_detaching.go | 9 + .../evs/extensions/volumeactions/detach.go | 18 ++ .../extensions/volumeactions/extend_size.go | 18 ++ .../extensions/volumeactions/force_delete.go | 10 + .../volumeactions/initialize_connection.go | 38 +++ .../evs/extensions/volumeactions/reserve.go | 11 + .../volumeactions/terminate_connection.go | 30 ++ .../volumeactions/testing/fixtures.go | 291 ++++++++++++++++++ .../volumeactions/testing/requests_test.go | 167 ++++++++++ .../evs/extensions/volumeactions/unreserve.go | 11 + .../extensions/volumeactions/upload_image.go | 123 ++++++++ openstack/evs/extensions/volumetenants.go | 6 + .../evs/noauth/new_block_storage_no_auth.go | 48 +++ openstack/evs/noauth/testing/fixtures.go | 19 ++ openstack/evs/noauth/testing/requests_test.go | 42 +++ 28 files changed, 1697 insertions(+) create mode 100644 openstack/evs/extensions/quotasets/delete.go create mode 100644 openstack/evs/extensions/quotasets/get.go create mode 100644 openstack/evs/extensions/quotasets/get_usage.go create mode 100644 openstack/evs/extensions/quotasets/testing/fixtures.go create mode 100644 openstack/evs/extensions/quotasets/testing/requests_test.go create mode 100644 openstack/evs/extensions/quotasets/update.go create mode 100644 openstack/evs/extensions/schedulerstats/list.go create mode 100644 openstack/evs/extensions/schedulerstats/testing/fixtures.go create mode 100644 openstack/evs/extensions/schedulerstats/testing/requests_test.go create mode 100644 openstack/evs/extensions/services/list.go create mode 100644 openstack/evs/extensions/services/testing/fixtures.go create mode 100644 openstack/evs/extensions/services/testing/requests_test.go create mode 100644 openstack/evs/extensions/volumeactions/attach.go create mode 100644 openstack/evs/extensions/volumeactions/begin_detaching.go create mode 100644 openstack/evs/extensions/volumeactions/detach.go create mode 100644 openstack/evs/extensions/volumeactions/extend_size.go create mode 100644 openstack/evs/extensions/volumeactions/force_delete.go create mode 100644 openstack/evs/extensions/volumeactions/initialize_connection.go create mode 100644 openstack/evs/extensions/volumeactions/reserve.go create mode 100644 openstack/evs/extensions/volumeactions/terminate_connection.go create mode 100644 openstack/evs/extensions/volumeactions/testing/fixtures.go create mode 100644 openstack/evs/extensions/volumeactions/testing/requests_test.go create mode 100644 openstack/evs/extensions/volumeactions/unreserve.go create mode 100644 openstack/evs/extensions/volumeactions/upload_image.go create mode 100644 openstack/evs/extensions/volumetenants.go create mode 100644 openstack/evs/noauth/new_block_storage_no_auth.go create mode 100644 openstack/evs/noauth/testing/fixtures.go create mode 100644 openstack/evs/noauth/testing/requests_test.go diff --git a/openstack/evs/extensions/quotasets/delete.go b/openstack/evs/extensions/quotasets/delete.go new file mode 100644 index 000000000..1ccb8edc6 --- /dev/null +++ b/openstack/evs/extensions/quotasets/delete.go @@ -0,0 +1,10 @@ +package quotasets + +import "github.com/opentelekomcloud/gophertelekomcloud" + +func Delete(client *golangsdk.ServiceClient, projectID string) (err error) { + _, err = client.Delete(client.ServiceURL("os-quota-sets", projectID), &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/evs/extensions/quotasets/get.go b/openstack/evs/extensions/quotasets/get.go new file mode 100644 index 000000000..e95b03b7b --- /dev/null +++ b/openstack/evs/extensions/quotasets/get.go @@ -0,0 +1,50 @@ +package quotasets + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +func Get(client *golangsdk.ServiceClient, projectID string) (*QuotaSet, error) { + raw, err := client.Get(client.ServiceURL("os-quota-sets", projectID), nil, nil) + if err != nil { + return nil, err + } + + var res struct { + QuotaSet QuotaSet `json:"quota_set"` + } + err = extract.Into(raw.Body, &res) + return &res.QuotaSet, err +} + +func GetDefaults(client *golangsdk.ServiceClient, projectID string) (*QuotaSet, error) { + raw, err := client.Get(client.ServiceURL("os-quota-sets", projectID, "defaults"), nil, nil) + if err != nil { + return nil, err + } + + var res QuotaSet + err = extract.IntoStructPtr(raw.Body, &res, "quota_set") + return &res, err +} + +type QuotaSet struct { + // ID is project associated with this QuotaSet. + ID string `json:"id"` + // Volumes is the number of volumes that are allowed for each project. + Volumes int `json:"volumes"` + // Snapshots is the number of snapshots that are allowed for each project. + Snapshots int `json:"snapshots"` + // Gigabytes is the size (GB) of volumes and snapshots that are allowed for + // each project. + Gigabytes int `json:"gigabytes"` + // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are + // allowed for each project and the specifed volume type. + PerVolumeGigabytes int `json:"per_volume_gigabytes"` + // Backups is the number of backups that are allowed for each project. + Backups int `json:"backups"` + // BackupGigabytes is the size (GB) of backups that are allowed for each + // project. + BackupGigabytes int `json:"backup_gigabytes"` +} diff --git a/openstack/evs/extensions/quotasets/get_usage.go b/openstack/evs/extensions/quotasets/get_usage.go new file mode 100644 index 000000000..4cfb66e89 --- /dev/null +++ b/openstack/evs/extensions/quotasets/get_usage.go @@ -0,0 +1,65 @@ +package quotasets + +import ( + "fmt" + + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +func GetUsage(client *golangsdk.ServiceClient, projectID string) (*QuotaUsageSet, error) { + raw, err := client.Get(fmt.Sprintf("%s?usage=true", client.ServiceURL("os-quota-sets", projectID)), nil, nil) + if err != nil { + return nil, err + } + + var res QuotaUsageSet + err = extract.IntoStructPtr(raw.Body, &res, "quota_set") + return &res, err +} + +type QuotaUsageSet struct { + // ID is the project ID associated with this QuotaUsageSet. + ID string `json:"id"` + // Volumes is the volume usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Volumes QuotaUsage `json:"volumes"` + // Snapshots is the snapshot usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Snapshots QuotaUsage `json:"snapshots"` + // Gigabytes is the size (GB) usage information of volumes and snapshots + // for this project, including in_use, limit, reserved and allocated + // attributes. Note: allocated attribute is available only when nested + // quota is enabled. + Gigabytes QuotaUsage `json:"gigabytes"` + // PerVolumeGigabytes is the size (GB) usage information for each volume, + // including in_use, limit, reserved and allocated attributes. Note: + // allocated attribute is available only when nested quota is enabled and + // only limit is meaningful here. + PerVolumeGigabytes QuotaUsage `json:"per_volume_gigabytes"` + // Backups is the backup usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Backups QuotaUsage `json:"backups"` + // BackupGigabytes is the size (GB) usage information of backup for this + // project, including in_use, limit, reserved and allocated attributes. + // Note: allocated attribute is available only when nested quota is + // enabled. + BackupGigabytes QuotaUsage `json:"backup_gigabytes"` +} + +type QuotaUsage struct { + // InUse is the current number of provisioned resources of the given type. + InUse int `json:"in_use"` + // Allocated is the current number of resources of a given type allocated + // for use. It is only available when nested quota is enabled. + Allocated int `json:"allocated"` + // Reserved is a transitional state when a claim against quota has been made + // but the resource is not yet fully online. + Reserved int `json:"reserved"` + // Limit is the maximum number of a given resource that can be + // allocated/provisioned. This is what "quota" usually refers to. + Limit int `json:"limit"` +} diff --git a/openstack/evs/extensions/quotasets/testing/fixtures.go b/openstack/evs/extensions/quotasets/testing/fixtures.go new file mode 100644 index 000000000..e7185bb8b --- /dev/null +++ b/openstack/evs/extensions/quotasets/testing/fixtures.go @@ -0,0 +1,163 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/quotasets" + + "github.com/opentelekomcloud/gophertelekomcloud" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" + "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" +) + +const FirstTenantID = "555544443333222211110000ffffeeee" + +var getExpectedJSONBody = ` +{ + "quota_set" : { + "volumes" : 8, + "snapshots" : 9, + "gigabytes" : 10, + "per_volume_gigabytes" : 11, + "backups" : 12, + "backup_gigabytes" : 13 + } +}` + +var getExpectedQuotaSet = quotasets.QuotaSet{ + Volumes: 8, + Snapshots: 9, + Gigabytes: 10, + PerVolumeGigabytes: 11, + Backups: 12, + BackupGigabytes: 13, +} + +var getUsageExpectedJSONBody = ` +{ + "quota_set": { + "id": "555544443333222211110000ffffeeee", + "volumes": { + "in_use": 15, + "limit": 16, + "reserved": 17 + }, + "snapshots": { + "in_use": 18, + "limit": 19, + "reserved": 20 + }, + "gigabytes": { + "in_use": 21, + "limit": 22, + "reserved": 23 + }, + "per_volume_gigabytes": { + "in_use": 24, + "limit": 25, + "reserved": 26 + }, + "backups": { + "in_use": 27, + "limit": 28, + "reserved": 29 + }, + "backup_gigabytes": { + "in_use": 30, + "limit": 31, + "reserved": 32 + } + } +} +` + +var getUsageExpectedQuotaSet = quotasets.QuotaUsageSet{ + ID: FirstTenantID, + Volumes: quotasets.QuotaUsage{InUse: 15, Limit: 16, Reserved: 17}, + Snapshots: quotasets.QuotaUsage{InUse: 18, Limit: 19, Reserved: 20}, + Gigabytes: quotasets.QuotaUsage{InUse: 21, Limit: 22, Reserved: 23}, + PerVolumeGigabytes: quotasets.QuotaUsage{InUse: 24, Limit: 25, Reserved: 26}, + Backups: quotasets.QuotaUsage{InUse: 27, Limit: 28, Reserved: 29}, + BackupGigabytes: quotasets.QuotaUsage{InUse: 30, Limit: 31, Reserved: 32}, +} + +var fullUpdateExpectedJSONBody = ` +{ + "quota_set": { + "volumes": 8, + "snapshots": 9, + "gigabytes": 10, + "per_volume_gigabytes": 11, + "backups": 12, + "backup_gigabytes": 13 + } +}` + +var fullUpdateOpts = quotasets.UpdateOpts{ + Volumes: golangsdk.IntToPointer(8), + Snapshots: golangsdk.IntToPointer(9), + Gigabytes: golangsdk.IntToPointer(10), + PerVolumeGigabytes: golangsdk.IntToPointer(11), + Backups: golangsdk.IntToPointer(12), + BackupGigabytes: golangsdk.IntToPointer(13), +} + +var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ + Volumes: 8, + Snapshots: 9, + Gigabytes: 10, + PerVolumeGigabytes: 11, + Backups: 12, + BackupGigabytes: 13, +} + +var partialUpdateExpectedJSONBody = ` +{ + "quota_set": { + "volumes": 200, + "snapshots": 0, + "gigabytes": 0, + "per_volume_gigabytes": 0, + "backups": 0, + "backup_gigabytes": 0 + } +}` + +var partialUpdateOpts = quotasets.UpdateOpts{ + Volumes: golangsdk.IntToPointer(200), + Snapshots: golangsdk.IntToPointer(0), + Gigabytes: golangsdk.IntToPointer(0), + PerVolumeGigabytes: golangsdk.IntToPointer(0), + Backups: golangsdk.IntToPointer(0), + BackupGigabytes: golangsdk.IntToPointer(0), +} + +var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{Volumes: 200} + +// HandleSuccessfulRequest configures the test server to respond to an HTTP request. +func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput string, uriQueryParams map[string]string) { + + th.Mux.HandleFunc(uriPath, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, httpMethod) + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + + if uriQueryParams != nil { + th.TestFormValues(t, r, uriQueryParams) + } + + _, _ = fmt.Fprint(w, jsonOutput) + }) +} + +// HandleDeleteSuccessfully tests quotaset deletion. +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusOK) + }) +} diff --git a/openstack/evs/extensions/quotasets/testing/requests_test.go b/openstack/evs/extensions/quotasets/testing/requests_test.go new file mode 100644 index 000000000..ccb67f398 --- /dev/null +++ b/openstack/evs/extensions/quotasets/testing/requests_test.go @@ -0,0 +1,63 @@ +package testing + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/quotasets" + + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" + "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" +) + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + uriQueryParms := map[string]string{} + HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getExpectedJSONBody, uriQueryParms) + actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &getExpectedQuotaSet, actual) +} + +func TestGetUsage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + uriQueryParms := map[string]string{"usage": "true"} + HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getUsageExpectedJSONBody, uriQueryParms) + actual, err := quotasets.GetUsage(client.ServiceClient(), FirstTenantID) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &getUsageExpectedQuotaSet, actual) +} + +func TestFullUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + uriQueryParms := map[string]string{} + HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, fullUpdateExpectedJSONBody, uriQueryParms) + actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, fullUpdateOpts) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &fullUpdateExpectedQuotaSet, actual) +} + +func TestPartialUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + uriQueryParms := map[string]string{} + HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, partialUpdateExpectedJSONBody, uriQueryParms) + actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, partialUpdateOpts) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &partiualUpdateExpectedQuotaSet, actual) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSuccessfully(t) + + err := quotasets.Delete(client.ServiceClient(), FirstTenantID) + th.AssertNoErr(t, err) +} diff --git a/openstack/evs/extensions/quotasets/update.go b/openstack/evs/extensions/quotasets/update.go new file mode 100644 index 000000000..aef568063 --- /dev/null +++ b/openstack/evs/extensions/quotasets/update.go @@ -0,0 +1,47 @@ +package quotasets + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +func Update(client *golangsdk.ServiceClient, projectID string, opts UpdateOpts) (*QuotaSet, error) { + b, err := golangsdk.BuildRequestBody(opts, "quota_set") + if err != nil { + return nil, err + } + + raw, err := client.Put(client.ServiceURL("os-quota-sets", projectID), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + if err != nil { + return nil, err + } + + var res QuotaSet + err = extract.IntoStructPtr(raw.Body, &res, "quota_set") + return &res, err +} + +type UpdateOpts struct { + // Volumes is the number of volumes that are allowed for each project. + Volumes *int `json:"volumes,omitempty"` + // Snapshots is the number of snapshots that are allowed for each project. + Snapshots *int `json:"snapshots,omitempty"` + // Gigabytes is the size (GB) of volumes and snapshots that are allowed for + // each project. + Gigabytes *int `json:"gigabytes,omitempty"` + // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are + // allowed for each project and the specifed volume type. + PerVolumeGigabytes *int `json:"per_volume_gigabytes,omitempty"` + // Backups is the number of backups that are allowed for each project. + Backups *int `json:"backups,omitempty"` + // BackupGigabytes is the size (GB) of backups that are allowed for each + // project. + BackupGigabytes *int `json:"backup_gigabytes,omitempty"` + // Groups is the number of groups that are allowed for each project. + Groups *int `json:"groups,omitempty"` + // Force will update the quotaset even if the quota has already been used + // and the reserved quota exceeds the new quota. + Force bool `json:"force,omitempty"` +} diff --git a/openstack/evs/extensions/schedulerstats/list.go b/openstack/evs/extensions/schedulerstats/list.go new file mode 100644 index 000000000..0e661bb4c --- /dev/null +++ b/openstack/evs/extensions/schedulerstats/list.go @@ -0,0 +1,96 @@ +package schedulerstats + +import ( + "encoding/json" + "math" + + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +type ListOpts struct { + // ID of the tenant to look up storage pools for. + TenantID string `q:"tenant_id"` + // Whether to list extended details. + Detail bool `q:"detail"` +} + +func List(client *golangsdk.ServiceClient, opts ListOpts) ([]StoragePool, error) { + query, err := golangsdk.BuildQueryString(opts) + if err != nil { + return nil, err + } + + raw, err := client.Get(client.ServiceURL("scheduler-stats", "get_pools")+query.String(), nil, nil) + if err != nil { + return nil, err + } + + var res []StoragePool + err = extract.IntoSlicePtr(raw.Body, &res, "pools") + return res, err +} + +type StoragePool struct { + Name string `json:"name"` + Capabilities Capabilities `json:"capabilities"` +} + +type Capabilities struct { + // The following fields should be present in all storage drivers. + DriverVersion string `json:"driver_version"` + FreeCapacityGB float64 `json:"-"` + StorageProtocol string `json:"storage_protocol"` + TotalCapacityGB float64 `json:"-"` + VendorName string `json:"vendor_name"` + VolumeBackendName string `json:"volume_backend_name"` + // The following fields are optional and may have empty values depending + // on the storage driver in use. + ReservedPercentage int64 `json:"reserved_percentage"` + LocationInfo string `json:"location_info"` + QoSSupport bool `json:"QoS_support"` + ProvisionedCapacityGB float64 `json:"provisioned_capacity_gb"` + MaxOverSubscriptionRatio string `json:"max_over_subscription_ratio"` + ThinProvisioningSupport bool `json:"thin_provisioning_support"` + ThickProvisioningSupport bool `json:"thick_provisioning_support"` + TotalVolumes int64 `json:"total_volumes"` + FilterFunction string `json:"filter_function"` + GoodnessFuction string `json:"goodness_function"` + Mutliattach bool `json:"multiattach"` + SparseCopyVolume bool `json:"sparse_copy_volume"` +} + +func (r *Capabilities) UnmarshalJSON(b []byte) error { + type tmp Capabilities + var s struct { + tmp + FreeCapacityGB interface{} `json:"free_capacity_gb"` + TotalCapacityGB interface{} `json:"total_capacity_gb"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Capabilities(s.tmp) + + // Generic function to parse a capacity value which may be a numeric + // value, "unknown", or "infinite" + parseCapacity := func(capacity interface{}) float64 { + if capacity != nil { + switch capacity := capacity.(type) { + case float64: + return capacity + case string: + if capacity == "infinite" { + return math.Inf(1) + } + } + } + return 0.0 + } + + r.FreeCapacityGB = parseCapacity(s.FreeCapacityGB) + r.TotalCapacityGB = parseCapacity(s.TotalCapacityGB) + + return nil +} diff --git a/openstack/evs/extensions/schedulerstats/testing/fixtures.go b/openstack/evs/extensions/schedulerstats/testing/fixtures.go new file mode 100644 index 000000000..02a015c1d --- /dev/null +++ b/openstack/evs/extensions/schedulerstats/testing/fixtures.go @@ -0,0 +1,107 @@ +package testing + +import ( + "fmt" + "math" + "net/http" + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/schedulerstats" + + "github.com/opentelekomcloud/gophertelekomcloud/testhelper" + "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" +) + +const StoragePoolsListBody = ` +{ + "pools": [ + { + "name": "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd" + }, + { + "name": "rbd:cinder.volumes.hdd@cinder.volumes#cinder.volumes.hdd" + } + ] +} +` + +const StoragePoolsListBodyDetail = ` +{ + "pools": [ + { + "capabilities": { + "driver_version": "1.2.0", + "filter_function": null, + "free_capacity_gb": 64765, + "goodness_function": null, + "multiattach": false, + "reserved_percentage": 0, + "storage_protocol": "ceph", + "timestamp": "2016-11-24T10:33:51.248360", + "total_capacity_gb": 787947.93, + "vendor_name": "Open Source", + "volume_backend_name": "cinder.volumes.ssd" + }, + "name": "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd" + }, + { + "capabilities": { + "driver_version": "1.2.0", + "filter_function": null, + "free_capacity_gb": "unknown", + "goodness_function": null, + "multiattach": false, + "reserved_percentage": 0, + "storage_protocol": "ceph", + "timestamp": "2016-11-24T10:33:43.138628", + "total_capacity_gb": "infinite", + "vendor_name": "Open Source", + "volume_backend_name": "cinder.volumes.hdd" + }, + "name": "rbd:cinder.volumes.hdd@cinder.volumes.hdd#cinder.volumes.hdd" + } + ] +} +` + +var ( + StoragePoolFake1 = schedulerstats.StoragePool{ + Name: "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd", + Capabilities: schedulerstats.Capabilities{ + DriverVersion: "1.2.0", + FreeCapacityGB: 64765, + StorageProtocol: "ceph", + TotalCapacityGB: 787947.93, + VendorName: "Open Source", + VolumeBackendName: "cinder.volumes.ssd", + }, + } + + StoragePoolFake2 = schedulerstats.StoragePool{ + Name: "rbd:cinder.volumes.hdd@cinder.volumes.hdd#cinder.volumes.hdd", + Capabilities: schedulerstats.Capabilities{ + DriverVersion: "1.2.0", + FreeCapacityGB: 0.0, + StorageProtocol: "ceph", + TotalCapacityGB: math.Inf(1), + VendorName: "Open Source", + VolumeBackendName: "cinder.volumes.hdd", + }, + } +) + +func HandleStoragePoolsListSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/scheduler-stats/get_pools", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + + _ = r.ParseForm() + if r.FormValue("detail") == "true" { + _, _ = fmt.Fprint(w, StoragePoolsListBodyDetail) + } else { + _, _ = fmt.Fprint(w, StoragePoolsListBody) + } + }) +} diff --git a/openstack/evs/extensions/schedulerstats/testing/requests_test.go b/openstack/evs/extensions/schedulerstats/testing/requests_test.go new file mode 100644 index 000000000..ae6f64147 --- /dev/null +++ b/openstack/evs/extensions/schedulerstats/testing/requests_test.go @@ -0,0 +1,25 @@ +package testing + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/schedulerstats" + + "github.com/opentelekomcloud/gophertelekomcloud/testhelper" + "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" +) + +func TestListStoragePoolsDetail(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleStoragePoolsListSuccessfully(t) + + actual, err := schedulerstats.List(client.ServiceClient(), schedulerstats.ListOpts{Detail: true}) + testhelper.AssertNoErr(t, err) + + if len(actual) != 2 { + t.Fatalf("Expected 2 backends, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, StoragePoolFake1, actual[0]) + testhelper.CheckDeepEquals(t, StoragePoolFake2, actual[1]) +} diff --git a/openstack/evs/extensions/services/list.go b/openstack/evs/extensions/services/list.go new file mode 100644 index 000000000..ef2755f21 --- /dev/null +++ b/openstack/evs/extensions/services/list.go @@ -0,0 +1,75 @@ +package services + +import ( + "encoding/json" + "time" + + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +type ListOpts struct { + // Filter the service list result by binary name of the service. + Binary string `q:"binary"` + // Filter the service list result by host name of the service. + Host string `q:"host"` +} + +func List(client *golangsdk.ServiceClient, opts ListOpts) ([]Service, error) { + query, err := golangsdk.BuildQueryString(opts) + if err != nil { + return nil, err + } + + raw, err := client.Get(client.ServiceURL("os-services")+query.String(), nil, nil) + if err != nil { + return nil, err + } + + var res []Service + err = extract.IntoSlicePtr(raw.Body, &res, "services") + return res, err +} + +type Service struct { + // The binary name of the service. + Binary string `json:"binary"` + // The reason for disabling a service. + DisabledReason string `json:"disabled_reason"` + // The name of the host. + Host string `json:"host"` + // The state of the service. One of up or down. + State string `json:"state"` + // The status of the service. One of available or unavailable. + Status string `json:"status"` + // The date and time stamp when the extension was last updated. + UpdatedAt time.Time `json:"-"` + // The availability zone name. + Zone string `json:"zone"` + // The following fields are optional + // The host is frozen or not. Only in cinder-volume service. + Frozen bool `json:"frozen"` + // The cluster name. Only in cinder-volume service. + Cluster string `json:"cluster"` + // The volume service replication status. Only in cinder-volume service. + ReplicationStatus string `json:"replication_status"` + // The ID of active storage backend. Only in cinder-volume service. + ActiveBackendID string `json:"active_backend_id"` +} + +func (r *Service) UnmarshalJSON(b []byte) error { + type tmp Service + var res struct { + tmp + UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &res) + if err != nil { + return err + } + *r = Service(res.tmp) + + r.UpdatedAt = time.Time(res.UpdatedAt) + + return nil +} diff --git a/openstack/evs/extensions/services/testing/fixtures.go b/openstack/evs/extensions/services/testing/fixtures.go new file mode 100644 index 000000000..c98b11b0b --- /dev/null +++ b/openstack/evs/extensions/services/testing/fixtures.go @@ -0,0 +1,98 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/services" + + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" + "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" +) + +// ServiceListBody is sample response to the List call +const ServiceListBody = ` +{ + "services": [{ + "status": "enabled", + "binary": "cinder-scheduler", + "zone": "nova", + "state": "up", + "updated_at": "2017-06-29T05:50:35.000000", + "host": "devstack", + "disabled_reason": null + }, + { + "status": "enabled", + "binary": "cinder-backup", + "zone": "nova", + "state": "up", + "updated_at": "2017-06-29T05:50:42.000000", + "host": "devstack", + "disabled_reason": null + }, + { + "status": "enabled", + "binary": "cinder-volume", + "zone": "nova", + "frozen": false, + "state": "up", + "updated_at": "2017-06-29T05:50:39.000000", + "cluster": null, + "host": "devstack@lvmdriver-1", + "replication_status": "disabled", + "active_backend_id": null, + "disabled_reason": null + }] +} +` + +// First service from the ServiceListBody +var FirstFakeService = services.Service{ + Binary: "cinder-scheduler", + DisabledReason: "", + Host: "devstack", + State: "up", + Status: "enabled", + UpdatedAt: time.Date(2017, 6, 29, 5, 50, 35, 0, time.UTC), + Zone: "nova", +} + +// Second service from the ServiceListBody +var SecondFakeService = services.Service{ + Binary: "cinder-backup", + DisabledReason: "", + Host: "devstack", + State: "up", + Status: "enabled", + UpdatedAt: time.Date(2017, 6, 29, 5, 50, 42, 0, time.UTC), + Zone: "nova", +} + +// Third service from the ServiceListBody +var ThirdFakeService = services.Service{ + ActiveBackendID: "", + Binary: "cinder-volume", + Cluster: "", + DisabledReason: "", + Frozen: false, + Host: "devstack@lvmdriver-1", + ReplicationStatus: "disabled", + State: "up", + Status: "enabled", + UpdatedAt: time.Date(2017, 6, 29, 5, 50, 39, 0, time.UTC), + Zone: "nova", +} + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-services", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + _, _ = fmt.Fprint(w, ServiceListBody) + }) +} diff --git a/openstack/evs/extensions/services/testing/requests_test.go b/openstack/evs/extensions/services/testing/requests_test.go new file mode 100644 index 000000000..7d06b67d1 --- /dev/null +++ b/openstack/evs/extensions/services/testing/requests_test.go @@ -0,0 +1,26 @@ +package testing + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/services" + + "github.com/opentelekomcloud/gophertelekomcloud/testhelper" + "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" +) + +func TestListServices(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleListSuccessfully(t) + + actual, err := services.List(client.ServiceClient(), services.ListOpts{}) + testhelper.AssertNoErr(t, err) + + if len(actual) != 3 { + t.Fatalf("Expected 3 services, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) + testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) + testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) +} diff --git a/openstack/evs/extensions/volumeactions/attach.go b/openstack/evs/extensions/volumeactions/attach.go new file mode 100644 index 000000000..0d176c4e3 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/attach.go @@ -0,0 +1,31 @@ +package volumeactions + +import "github.com/opentelekomcloud/gophertelekomcloud" + +type AttachMode string + +const ( + ReadOnly AttachMode = "ro" + ReadWrite AttachMode = "rw" +) + +type AttachOpts struct { + // The mountpoint of this volume. + MountPoint string `json:"mountpoint,omitempty"` + // The nova instance ID, can't set simultaneously with HostName. + InstanceUUID string `json:"instance_uuid,omitempty"` + // The hostname of baremetal host, can't set simultaneously with InstanceUUID. + HostName string `json:"host_name,omitempty"` + // Mount mode of this volume. + Mode AttachMode `json:"mode,omitempty"` +} + +func Attach(client *golangsdk.ServiceClient, id string, opts AttachOpts) (err error) { + b, err := golangsdk.BuildRequestBody(opts, "os-attach") + if err != nil { + return + } + + _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, nil) + return +} diff --git a/openstack/evs/extensions/volumeactions/begin_detaching.go b/openstack/evs/extensions/volumeactions/begin_detaching.go new file mode 100644 index 000000000..0835090ad --- /dev/null +++ b/openstack/evs/extensions/volumeactions/begin_detaching.go @@ -0,0 +1,9 @@ +package volumeactions + +import "github.com/opentelekomcloud/gophertelekomcloud" + +func BeginDetaching(client *golangsdk.ServiceClient, id string) (err error) { + b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} + _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, nil) + return +} diff --git a/openstack/evs/extensions/volumeactions/detach.go b/openstack/evs/extensions/volumeactions/detach.go new file mode 100644 index 000000000..cd54d2780 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/detach.go @@ -0,0 +1,18 @@ +package volumeactions + +import "github.com/opentelekomcloud/gophertelekomcloud" + +type DetachOpts struct { + // AttachmentID is the ID of the attachment between a volume and instance. + AttachmentID string `json:"attachment_id,omitempty"` +} + +func Detach(client *golangsdk.ServiceClient, id string, opts DetachOpts) (err error) { + b, err := golangsdk.BuildRequestBody(opts, "os-detach") + if err != nil { + return + } + + _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, nil) + return +} diff --git a/openstack/evs/extensions/volumeactions/extend_size.go b/openstack/evs/extensions/volumeactions/extend_size.go new file mode 100644 index 000000000..28a24f75b --- /dev/null +++ b/openstack/evs/extensions/volumeactions/extend_size.go @@ -0,0 +1,18 @@ +package volumeactions + +import "github.com/opentelekomcloud/gophertelekomcloud" + +type ExtendSizeOpts struct { + // NewSize is the new size of the volume, in GB. + NewSize int `json:"new_size" required:"true"` +} + +func ExtendSize(client *golangsdk.ServiceClient, id string, opts ExtendSizeOpts) (err error) { + b, err := golangsdk.BuildRequestBody(opts, "os-extend") + if err != nil { + return + } + + _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, nil) + return +} diff --git a/openstack/evs/extensions/volumeactions/force_delete.go b/openstack/evs/extensions/volumeactions/force_delete.go new file mode 100644 index 000000000..7082b4ff9 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/force_delete.go @@ -0,0 +1,10 @@ +package volumeactions + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" +) + +func ForceDelete(client *golangsdk.ServiceClient, id string) (err error) { + _, err = client.Post(client.ServiceURL("volumes", id, "action"), map[string]interface{}{"os-force_delete": ""}, nil, nil) + return +} diff --git a/openstack/evs/extensions/volumeactions/initialize_connection.go b/openstack/evs/extensions/volumeactions/initialize_connection.go new file mode 100644 index 000000000..91ae63629 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/initialize_connection.go @@ -0,0 +1,38 @@ +package volumeactions + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +type InitializeConnectionOpts struct { + IP string `json:"ip,omitempty"` + Host string `json:"host,omitempty"` + Initiator string `json:"initiator,omitempty"` + Wwpns []string `json:"wwpns,omitempty"` + Wwnns string `json:"wwnns,omitempty"` + Multipath *bool `json:"multipath,omitempty"` + Platform string `json:"platform,omitempty"` + OSType string `json:"os_type,omitempty"` +} + +func InitializeConnection(client *golangsdk.ServiceClient, id string, opts InitializeConnectionOpts) (map[string]interface{}, error) { + b, err := golangsdk.BuildRequestBody(opts, "connector") + b = map[string]interface{}{"os-initialize_connection": b} + if err != nil { + return nil, err + } + + raw, err := client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if err != nil { + return nil, err + } + + var res struct { + ConnectionInfo map[string]interface{} `json:"connection_info"` + } + err = extract.Into(raw.Body, &res) + return res.ConnectionInfo, err +} diff --git a/openstack/evs/extensions/volumeactions/reserve.go b/openstack/evs/extensions/volumeactions/reserve.go new file mode 100644 index 000000000..19bb18293 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/reserve.go @@ -0,0 +1,11 @@ +package volumeactions + +import "github.com/opentelekomcloud/gophertelekomcloud" + +func Reserve(client *golangsdk.ServiceClient, id string) (err error) { + b := map[string]interface{}{"os-reserve": make(map[string]interface{})} + _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + return +} diff --git a/openstack/evs/extensions/volumeactions/terminate_connection.go b/openstack/evs/extensions/volumeactions/terminate_connection.go new file mode 100644 index 000000000..781943259 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/terminate_connection.go @@ -0,0 +1,30 @@ +package volumeactions + +import "github.com/opentelekomcloud/gophertelekomcloud" + +type TerminateConnectionOpts struct { + IP string `json:"ip,omitempty"` + Host string `json:"host,omitempty"` + Initiator string `json:"initiator,omitempty"` + Wwpns []string `json:"wwpns,omitempty"` + Wwnns string `json:"wwnns,omitempty"` + Multipath *bool `json:"multipath,omitempty"` + Platform string `json:"platform,omitempty"` + OSType string `json:"os_type,omitempty"` +} + +func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { + b, err := golangsdk.BuildRequestBody(opts, "connector") + return map[string]interface{}{"os-terminate_connection": b}, err +} + +func TerminateConnection(client *golangsdk.ServiceClient, id string, opts TerminateConnectionOpts) (err error) { + b, err := golangsdk.BuildRequestBody(opts, "connector") + b = map[string]interface{}{"os-terminate_connection": b} + if err != nil { + return + } + + _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, nil) + return +} diff --git a/openstack/evs/extensions/volumeactions/testing/fixtures.go b/openstack/evs/extensions/volumeactions/testing/fixtures.go new file mode 100644 index 000000000..fed8bc879 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/testing/fixtures.go @@ -0,0 +1,291 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" + fake "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" +) + +func MockAttachResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", + 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, ` +{ + "os-attach": + { + "mountpoint": "/mnt", + "mode": "rw", + "instance_uuid": "50902f4f-a974-46a0-85e9-7efc5e22dfdd" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + _, _ = fmt.Fprint(w, `{}`) + }) +} + +func MockBeginDetachingResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", + 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, ` +{ + "os-begin_detaching": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + _, _ = fmt.Fprint(w, `{}`) + }) +} + +func MockDetachResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", + 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, ` +{ + "os-detach": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + _, _ = fmt.Fprint(w, `{}`) + }) +} + +func MockUploadImageResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", + 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, ` +{ + "os-volume_upload_image": { + "container_format": "bare", + "force": true, + "image_name": "test", + "disk_format": "raw" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + _, _ = fmt.Fprint(w, ` +{ + "os-volume_upload_image": { + "container_format": "bare", + "display_description": null, + "id": "cd281d77-8217-4830-be95-9528227c105c", + "image_id": "ecb92d98-de08-45db-8235-bbafe317269c", + "image_name": "test", + "disk_format": "raw", + "size": 5, + "status": "uploading", + "updated_at": "2017-07-17T09:29:22.000000", + "volume_type": { + "created_at": "2016-05-04T08:54:14.000000", + "deleted": false, + "deleted_at": null, + "description": null, + "extra_specs": { + "volume_backend_name": "basic.ru-2a" + }, + "id": "b7133444-62f6-4433-8da3-70ac332229b7", + "is_public": true, + "name": "basic.ru-2a", + "updated_at": "2016-05-04T09:15:33.000000" + } + } +} + `) + }) +} + +func MockReserveResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", + 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, ` +{ + "os-reserve": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + _, _ = fmt.Fprint(w, `{}`) + }) +} + +func MockUnreserveResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", + 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, ` +{ + "os-unreserve": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + _, _ = fmt.Fprint(w, `{}`) + }) +} + +func MockInitializeConnectionResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", + 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, ` +{ + "os-initialize_connection": + { + "connector": + { + "ip":"127.0.0.1", + "host":"stack", + "initiator":"iqn.1994-05.com.redhat:17cf566367d2", + "multipath": false, + "platform": "x86_64", + "os_type": "linux2" + } + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + _, _ = fmt.Fprint(w, `{ +"connection_info": { + "data": { + "target_portals": [ + "172.31.17.48:3260" + ], + "auth_method": "CHAP", + "auth_username": "5MLtcsTEmNN5jFVcT6ui", + "access_mode": "rw", + "target_lun": 0, + "volume_id": "cd281d77-8217-4830-be95-9528227c105c", + "target_luns": [ + 0 + ], + "target_iqns": [ + "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c" + ], + "auth_password": "x854ZY5Re3aCkdNL", + "target_discovered": false, + "encrypted": false, + "qos_specs": null, + "target_iqn": "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c", + "target_portal": "172.31.17.48:3260" + }, + "driver_volume_type": "iscsi" + } + }`) + }) +} + +func MockTerminateConnectionResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", + 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, ` +{ + "os-terminate_connection": + { + "connector": + { + "ip":"127.0.0.1", + "host":"stack", + "initiator":"iqn.1994-05.com.redhat:17cf566367d2", + "multipath": true, + "platform": "x86_64", + "os_type": "linux2" + } + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + _, _ = fmt.Fprint(w, `{}`) + }) +} + +func MockExtendSizeResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", + 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, ` +{ + "os-extend": + { + "new_size": 3 + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + _, _ = fmt.Fprint(w, `{}`) + }) +} + +func MockForceDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, ` +{ + "os-force_delete": "" +} + `) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/evs/extensions/volumeactions/testing/requests_test.go b/openstack/evs/extensions/volumeactions/testing/requests_test.go new file mode 100644 index 000000000..95bb9d9cb --- /dev/null +++ b/openstack/evs/extensions/volumeactions/testing/requests_test.go @@ -0,0 +1,167 @@ +package testing + +import ( + "testing" + "time" + + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/volumeactions" + + "github.com/opentelekomcloud/gophertelekomcloud" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" + "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" +) + +func TestAttach(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockAttachResponse(t) + + options := volumeactions.AttachOpts{ + MountPoint: "/mnt", + Mode: "rw", + InstanceUUID: "50902f4f-a974-46a0-85e9-7efc5e22dfdd", + } + err := volumeactions.Attach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options) + th.AssertNoErr(t, err) +} + +func TestBeginDetaching(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockBeginDetachingResponse(t) + + err := volumeactions.BeginDetaching(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c") + th.AssertNoErr(t, err) +} + +func TestDetach(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDetachResponse(t) + + err := volumeactions.Detach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", volumeactions.DetachOpts{}) + th.AssertNoErr(t, err) +} + +func TestUploadImage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + MockUploadImageResponse(t) + options := volumeactions.UploadImageOpts{ + ContainerFormat: "bare", + DiskFormat: "raw", + ImageName: "test", + Force: true, + } + + actual, err := volumeactions.UploadImage(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options) + th.AssertNoErr(t, err) + + expected := volumeactions.VolumeImage{ + VolumeID: "cd281d77-8217-4830-be95-9528227c105c", + ContainerFormat: "bare", + DiskFormat: "raw", + Description: "", + ImageID: "ecb92d98-de08-45db-8235-bbafe317269c", + ImageName: "test", + Size: 5, + Status: "uploading", + UpdatedAt: time.Date(2017, 7, 17, 9, 29, 22, 0, time.UTC), + VolumeType: volumeactions.ImageVolumeType{ + ID: "b7133444-62f6-4433-8da3-70ac332229b7", + Name: "basic.ru-2a", + Description: "", + IsPublic: true, + ExtraSpecs: map[string]interface{}{"volume_backend_name": "basic.ru-2a"}, + QosSpecsID: "", + Deleted: false, + DeletedAt: time.Time{}, + CreatedAt: time.Date(2016, 5, 4, 8, 54, 14, 0, time.UTC), + UpdatedAt: time.Date(2016, 5, 4, 9, 15, 33, 0, time.UTC), + }, + } + th.AssertDeepEquals(t, &expected, actual) +} + +func TestReserve(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockReserveResponse(t) + + err := volumeactions.Reserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c") + th.AssertNoErr(t, err) +} + +func TestUnreserve(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUnreserveResponse(t) + + err := volumeactions.Unreserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c") + th.AssertNoErr(t, err) +} + +func TestInitializeConnection(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockInitializeConnectionResponse(t) + + options := volumeactions.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: golangsdk.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + _, err := volumeactions.InitializeConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options) + th.AssertNoErr(t, err) +} + +func TestTerminateConnection(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockTerminateConnectionResponse(t) + + options := volumeactions.TerminateConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: golangsdk.Enabled, + Platform: "x86_64", + OSType: "linux2", + } + err := volumeactions.TerminateConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options) + th.AssertNoErr(t, err) +} + +func TestExtendSize(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockExtendSizeResponse(t) + + options := volumeactions.ExtendSizeOpts{ + NewSize: 3, + } + + err := volumeactions.ExtendSize(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options) + th.AssertNoErr(t, err) +} + +func TestForceDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockForceDeleteResponse(t) + + res := volumeactions.ForceDelete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res) +} diff --git a/openstack/evs/extensions/volumeactions/unreserve.go b/openstack/evs/extensions/volumeactions/unreserve.go new file mode 100644 index 000000000..5eaf9ec96 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/unreserve.go @@ -0,0 +1,11 @@ +package volumeactions + +import "github.com/opentelekomcloud/gophertelekomcloud" + +func Unreserve(client *golangsdk.ServiceClient, id string) (err error) { + b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} + _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + return +} diff --git a/openstack/evs/extensions/volumeactions/upload_image.go b/openstack/evs/extensions/volumeactions/upload_image.go new file mode 100644 index 000000000..5b955f544 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/upload_image.go @@ -0,0 +1,123 @@ +package volumeactions + +import ( + "encoding/json" + "time" + + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +type UploadImageOpts struct { + // Container format, may be bare, ofv, ova, etc. + ContainerFormat string `json:"container_format,omitempty"` + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + DiskFormat string `json:"disk_format,omitempty"` + // The name of image that will be stored in glance. + ImageName string `json:"image_name,omitempty"` + // Force image creation, usable if volume attached to instance. + Force bool `json:"force,omitempty"` +} + +func UploadImage(client *golangsdk.ServiceClient, id string, opts UploadImageOpts) (*VolumeImage, error) { + b, err := golangsdk.BuildRequestBody(opts, "os-volume_upload_image") + if err != nil { + return nil, err + } + + raw, err := client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{202}, + }) + if err != nil { + return nil, err + } + + var res struct { + VolumeImage VolumeImage `json:"os-volume_upload_image"` + } + err = extract.Into(raw.Body, &res) + return &res.VolumeImage, err +} + +type VolumeImage struct { + // The ID of a volume an image is created from. + VolumeID string `json:"id"` + // Container format, may be bare, ofv, ova, etc. + ContainerFormat string `json:"container_format"` + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + DiskFormat string `json:"disk_format"` + // Human-readable description for the volume. + Description string `json:"display_description"` + // The ID of the created image. + ImageID string `json:"image_id"` + // Human-readable display name for the image. + ImageName string `json:"image_name"` + // Size of the volume in GB. + Size int `json:"size"` + // Current status of the volume. + Status string `json:"status"` + // The date when this volume was last updated. + UpdatedAt time.Time `json:"-"` + // Volume type object of used volume. + VolumeType ImageVolumeType `json:"volume_type"` +} + +func (r *VolumeImage) UnmarshalJSON(b []byte) error { + type tmp VolumeImage + var s struct { + tmp + UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = VolumeImage(s.tmp) + + r.UpdatedAt = time.Time(s.UpdatedAt) + return err +} + +type ImageVolumeType struct { + // The ID of a volume type. + ID string `json:"id"` + // Human-readable display name for the volume type. + Name string `json:"name"` + // Human-readable description for the volume type. + Description string `json:"display_description"` + // Flag for public access. + IsPublic bool `json:"is_public"` + // Extra specifications for volume type. + ExtraSpecs map[string]interface{} `json:"extra_specs"` + // ID of quality of service specs. + QosSpecsID string `json:"qos_specs_id"` + // Flag for deletion status of volume type. + Deleted bool `json:"deleted"` + // The date when volume type was deleted. + DeletedAt time.Time `json:"-"` + // The date when volume type was created. + CreatedAt time.Time `json:"-"` + // The date when this volume was last updated. + UpdatedAt time.Time `json:"-"` +} + +func (r *ImageVolumeType) UnmarshalJSON(b []byte) error { + type tmp ImageVolumeType + var s struct { + tmp + CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` + DeletedAt golangsdk.JSONRFC3339MilliNoZ `json:"deleted_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = ImageVolumeType(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.DeletedAt = time.Time(s.DeletedAt) + + return err +} diff --git a/openstack/evs/extensions/volumetenants.go b/openstack/evs/extensions/volumetenants.go new file mode 100644 index 000000000..50fdb32b4 --- /dev/null +++ b/openstack/evs/extensions/volumetenants.go @@ -0,0 +1,6 @@ +package extensions + +type VolumeTenantExt struct { + // TenantID is the id of the project that owns the volume. + TenantID string `json:"os-vol-tenant-attr:tenant_id"` +} diff --git a/openstack/evs/noauth/new_block_storage_no_auth.go b/openstack/evs/noauth/new_block_storage_no_auth.go new file mode 100644 index 000000000..bc7747811 --- /dev/null +++ b/openstack/evs/noauth/new_block_storage_no_auth.go @@ -0,0 +1,48 @@ +package noauth + +import ( + "fmt" + "strings" + + "github.com/opentelekomcloud/gophertelekomcloud" +) + +// EndpointOpts specifies a "noauth" Cinder Endpoint. +type EndpointOpts struct { + // CinderEndpoint [required] is currently only used with "noauth" Cinder. + // A cinder endpoint with "auth_strategy=noauth" is necessary, for example: + // http://example.com:8776/v2. + CinderEndpoint string +} + +// NewClient prepares an unauthenticated ProviderClient instance. +func NewClient(options golangsdk.AuthOptions) (*golangsdk.ProviderClient, error) { + if options.Username == "" { + options.Username = "admin" + } + if options.TenantName == "" { + options.TenantName = "admin" + } + + client := &golangsdk.ProviderClient{ + TokenID: fmt.Sprintf("%s:%s", options.Username, options.TenantName), + } + + return client, nil +} + +func NewBlockStorageNoAuth(client *golangsdk.ProviderClient, opts EndpointOpts) (*golangsdk.ServiceClient, error) { + sc := new(golangsdk.ServiceClient) + if opts.CinderEndpoint == "" { + return nil, fmt.Errorf("CinderEndpoint is required") + } + + token := strings.Split(client.TokenID, ":") + if len(token) != 2 { + return nil, fmt.Errorf("Malformed noauth token") + } + + sc.Endpoint = golangsdk.NormalizeURL(fmt.Sprintf("%s%s", golangsdk.NormalizeURL(opts.CinderEndpoint), token[1])) + sc.ProviderClient = client + return sc, nil +} diff --git a/openstack/evs/noauth/testing/fixtures.go b/openstack/evs/noauth/testing/fixtures.go new file mode 100644 index 000000000..f78bda3c5 --- /dev/null +++ b/openstack/evs/noauth/testing/fixtures.go @@ -0,0 +1,19 @@ +package testing + +// NoAuthResult is the expected result of the noauth Service Client +type NoAuthResult struct { + TokenID string + Endpoint string +} + +var naTestResult = NoAuthResult{ + TokenID: "user:test", + Endpoint: "http://cinder:8776/v2/test/", +} + +var naResult = NoAuthResult{ + TokenID: "admin:admin", + Endpoint: "http://cinder:8776/v2/admin/", +} + +var errorResult = "CinderEndpoint is required" diff --git a/openstack/evs/noauth/testing/requests_test.go b/openstack/evs/noauth/testing/requests_test.go new file mode 100644 index 000000000..651f16d71 --- /dev/null +++ b/openstack/evs/noauth/testing/requests_test.go @@ -0,0 +1,42 @@ +package testing + +import ( + "testing" + + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/noauth" + + "github.com/opentelekomcloud/gophertelekomcloud" + th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" +) + +func TestNoAuth(t *testing.T) { + ao := golangsdk.AuthOptions{ + Username: "user", + TenantName: "test", + } + provider, err := noauth.NewClient(ao) + th.AssertNoErr(t, err) + noauthClient, err := noauth.NewBlockStorageNoAuth(provider, noauth.EndpointOpts{ + CinderEndpoint: "http://cinder:8776/v2", + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, naTestResult.Endpoint, noauthClient.Endpoint) + th.AssertEquals(t, naTestResult.TokenID, noauthClient.TokenID) + + ao2 := golangsdk.AuthOptions{} + provider2, err := noauth.NewClient(ao2) + th.AssertNoErr(t, err) + noauthClient2, err := noauth.NewBlockStorageNoAuth(provider2, noauth.EndpointOpts{ + CinderEndpoint: "http://cinder:8776/v2/", + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, naResult.Endpoint, noauthClient2.Endpoint) + th.AssertEquals(t, naResult.TokenID, noauthClient2.TokenID) + + errTest, err := noauth.NewBlockStorageNoAuth(provider2, noauth.EndpointOpts{}) + _ = errTest + if err == nil { + t.Fatalf("Expected to receive error") + } + th.AssertEquals(t, errorResult, err.Error()) +} From 70cfe31f91b9540eef0cfc4437b38e8b1b020dd3 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 13:33:28 +0100 Subject: [PATCH 17/51] move --- acceptance/openstack/evs/{ => v3}/extensions/backups_test.go | 0 acceptance/openstack/evs/{ => v3}/extensions/extensions.go | 0 acceptance/openstack/evs/{ => v3}/extensions/limits_test.go | 0 .../openstack/evs/{ => v3}/extensions/schedulerhints_test.go | 0 .../openstack/evs/{ => v3}/extensions/schedulerstats_test.go | 0 acceptance/openstack/evs/{ => v3}/extensions/services_test.go | 0 .../openstack/evs/{ => v3}/extensions/volumeactions_test.go | 0 .../openstack/evs/{ => v3}/extensions/volumetenants_test.go | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename acceptance/openstack/evs/{ => v3}/extensions/backups_test.go (100%) rename acceptance/openstack/evs/{ => v3}/extensions/extensions.go (100%) rename acceptance/openstack/evs/{ => v3}/extensions/limits_test.go (100%) rename acceptance/openstack/evs/{ => v3}/extensions/schedulerhints_test.go (100%) rename acceptance/openstack/evs/{ => v3}/extensions/schedulerstats_test.go (100%) rename acceptance/openstack/evs/{ => v3}/extensions/services_test.go (100%) rename acceptance/openstack/evs/{ => v3}/extensions/volumeactions_test.go (100%) rename acceptance/openstack/evs/{ => v3}/extensions/volumetenants_test.go (100%) diff --git a/acceptance/openstack/evs/extensions/backups_test.go b/acceptance/openstack/evs/v3/extensions/backups_test.go similarity index 100% rename from acceptance/openstack/evs/extensions/backups_test.go rename to acceptance/openstack/evs/v3/extensions/backups_test.go diff --git a/acceptance/openstack/evs/extensions/extensions.go b/acceptance/openstack/evs/v3/extensions/extensions.go similarity index 100% rename from acceptance/openstack/evs/extensions/extensions.go rename to acceptance/openstack/evs/v3/extensions/extensions.go diff --git a/acceptance/openstack/evs/extensions/limits_test.go b/acceptance/openstack/evs/v3/extensions/limits_test.go similarity index 100% rename from acceptance/openstack/evs/extensions/limits_test.go rename to acceptance/openstack/evs/v3/extensions/limits_test.go diff --git a/acceptance/openstack/evs/extensions/schedulerhints_test.go b/acceptance/openstack/evs/v3/extensions/schedulerhints_test.go similarity index 100% rename from acceptance/openstack/evs/extensions/schedulerhints_test.go rename to acceptance/openstack/evs/v3/extensions/schedulerhints_test.go diff --git a/acceptance/openstack/evs/extensions/schedulerstats_test.go b/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go similarity index 100% rename from acceptance/openstack/evs/extensions/schedulerstats_test.go rename to acceptance/openstack/evs/v3/extensions/schedulerstats_test.go diff --git a/acceptance/openstack/evs/extensions/services_test.go b/acceptance/openstack/evs/v3/extensions/services_test.go similarity index 100% rename from acceptance/openstack/evs/extensions/services_test.go rename to acceptance/openstack/evs/v3/extensions/services_test.go diff --git a/acceptance/openstack/evs/extensions/volumeactions_test.go b/acceptance/openstack/evs/v3/extensions/volumeactions_test.go similarity index 100% rename from acceptance/openstack/evs/extensions/volumeactions_test.go rename to acceptance/openstack/evs/v3/extensions/volumeactions_test.go diff --git a/acceptance/openstack/evs/extensions/volumetenants_test.go b/acceptance/openstack/evs/v3/extensions/volumetenants_test.go similarity index 100% rename from acceptance/openstack/evs/extensions/volumetenants_test.go rename to acceptance/openstack/evs/v3/extensions/volumetenants_test.go From bff147512c58fbf90cc363f0a1ee73962f9401fa Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 15:18:08 +0100 Subject: [PATCH 18/51] List --- acceptance/openstack/evs/v3/blockstorage.go | 92 ---------- .../openstack/evs/v3/extensions/extensions.go | 49 +++--- .../evs/v3/extensions/schedulerstats_test.go | 9 +- acceptance/openstack/evs/v3/quotaset_test.go | 36 ++-- .../openstack/evs/v3/volumetypes_test.go | 162 ------------------ .../extensions/availabilityzones/doc.go | 0 .../extensions/availabilityzones/requests.go | 0 .../extensions/availabilityzones/results.go | 0 .../extensions/availabilityzones/urls.go | 0 .../evs/{v3 => }/extensions/backups/doc.go | 0 .../{v3 => }/extensions/backups/requests.go | 0 .../{v3 => }/extensions/backups/results.go | 0 .../evs/{v3 => }/extensions/backups/urls.go | 0 .../evs/{v3 => }/extensions/limits/doc.go | 0 .../{v3 => }/extensions/limits/requests.go | 0 .../evs/{v3 => }/extensions/limits/results.go | 0 .../evs/{v3 => }/extensions/limits/urls.go | 0 .../{v3 => }/extensions/quotasets/requests.go | 0 .../{v3 => }/extensions/quotasets/results.go | 0 .../evs/{v3 => }/extensions/quotasets/urls.go | 0 .../{v3 => }/extensions/schedulerhints/doc.go | 0 .../extensions/schedulerhints/requests.go | 0 .../extensions/schedulerstats/requests.go | 0 .../extensions/schedulerstats/results.go | 0 .../extensions/schedulerstats/urls.go | 0 .../extensions/volumeactions/requests.go | 0 .../extensions/volumeactions/results.go | 0 .../{v3 => }/extensions/volumeactions/urls.go | 0 .../evs/{v3 => }/extensions/volumehost/doc.go | 0 .../{v3 => }/extensions/volumehost/results.go | 0 .../{v3 => }/extensions/volumetenants/doc.go | 0 .../extensions/volumetenants/results.go | 0 .../extensions/volumetransfers/doc.go | 0 .../extensions/volumetransfers/requests.go | 0 .../extensions/volumetransfers/results.go | 0 .../extensions/volumetransfers/urls.go | 0 openstack/evs/v3/extensions/quotasets/doc.go | 60 ------- .../evs/v3/extensions/schedulerstats/doc.go | 23 --- openstack/evs/v3/extensions/services/doc.go | 22 --- .../evs/v3/extensions/services/requests.go | 42 ----- .../evs/v3/extensions/services/results.go | 84 --------- openstack/evs/v3/extensions/services/urls.go | 7 - .../evs/v3/extensions/volumeactions/doc.go | 108 ------------ openstack/evs/v3/volumetypes/List.go | 7 + 44 files changed, 53 insertions(+), 648 deletions(-) delete mode 100644 acceptance/openstack/evs/v3/volumetypes_test.go rename openstack/evs/{v3 => }/extensions/availabilityzones/doc.go (100%) rename openstack/evs/{v3 => }/extensions/availabilityzones/requests.go (100%) rename openstack/evs/{v3 => }/extensions/availabilityzones/results.go (100%) rename openstack/evs/{v3 => }/extensions/availabilityzones/urls.go (100%) rename openstack/evs/{v3 => }/extensions/backups/doc.go (100%) rename openstack/evs/{v3 => }/extensions/backups/requests.go (100%) rename openstack/evs/{v3 => }/extensions/backups/results.go (100%) rename openstack/evs/{v3 => }/extensions/backups/urls.go (100%) rename openstack/evs/{v3 => }/extensions/limits/doc.go (100%) rename openstack/evs/{v3 => }/extensions/limits/requests.go (100%) rename openstack/evs/{v3 => }/extensions/limits/results.go (100%) rename openstack/evs/{v3 => }/extensions/limits/urls.go (100%) rename openstack/evs/{v3 => }/extensions/quotasets/requests.go (100%) rename openstack/evs/{v3 => }/extensions/quotasets/results.go (100%) rename openstack/evs/{v3 => }/extensions/quotasets/urls.go (100%) rename openstack/evs/{v3 => }/extensions/schedulerhints/doc.go (100%) rename openstack/evs/{v3 => }/extensions/schedulerhints/requests.go (100%) rename openstack/evs/{v3 => }/extensions/schedulerstats/requests.go (100%) rename openstack/evs/{v3 => }/extensions/schedulerstats/results.go (100%) rename openstack/evs/{v3 => }/extensions/schedulerstats/urls.go (100%) rename openstack/evs/{v3 => }/extensions/volumeactions/requests.go (100%) rename openstack/evs/{v3 => }/extensions/volumeactions/results.go (100%) rename openstack/evs/{v3 => }/extensions/volumeactions/urls.go (100%) rename openstack/evs/{v3 => }/extensions/volumehost/doc.go (100%) rename openstack/evs/{v3 => }/extensions/volumehost/results.go (100%) rename openstack/evs/{v3 => }/extensions/volumetenants/doc.go (100%) rename openstack/evs/{v3 => }/extensions/volumetenants/results.go (100%) rename openstack/evs/{v3 => }/extensions/volumetransfers/doc.go (100%) rename openstack/evs/{v3 => }/extensions/volumetransfers/requests.go (100%) rename openstack/evs/{v3 => }/extensions/volumetransfers/results.go (100%) rename openstack/evs/{v3 => }/extensions/volumetransfers/urls.go (100%) delete mode 100644 openstack/evs/v3/extensions/quotasets/doc.go delete mode 100644 openstack/evs/v3/extensions/schedulerstats/doc.go delete mode 100644 openstack/evs/v3/extensions/services/doc.go delete mode 100644 openstack/evs/v3/extensions/services/requests.go delete mode 100644 openstack/evs/v3/extensions/services/results.go delete mode 100644 openstack/evs/v3/extensions/services/urls.go delete mode 100644 openstack/evs/v3/extensions/volumeactions/doc.go diff --git a/acceptance/openstack/evs/v3/blockstorage.go b/acceptance/openstack/evs/v3/blockstorage.go index 183fbf900..976e707a4 100644 --- a/acceptance/openstack/evs/v3/blockstorage.go +++ b/acceptance/openstack/evs/v3/blockstorage.go @@ -116,98 +116,6 @@ func CreateVolumeWithType(t *testing.T, client *golangsdk.ServiceClient, vt *vol return volume, nil } -// CreateVolumeType will create a volume type with a random name. An -// error will be returned if the volume was unable to be created. -func CreateVolumeType(t *testing.T, client *golangsdk.ServiceClient) (*volumetypes.VolumeType, error) { - name := tools.RandomString("ACPTTEST", 16) - description := "create_from_gophercloud" - t.Logf("Attempting to create volume type: %s", name) - - createOpts := volumetypes.CreateOpts{ - Name: name, - ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name"}, - Description: description, - } - - vt, err := volumetypes.Create(client, createOpts) - if err != nil { - return nil, err - } - - tools.PrintResource(t, vt) - th.AssertEquals(t, vt.IsPublic, true) - th.AssertEquals(t, vt.Name, name) - th.AssertEquals(t, vt.Description, description) - // TODO: For some reason returned extra_specs are empty even in API reference: https://developer.openstack.org/api-ref/block-storage/v3/?expanded=create-a-volume-type-detail#volume-types-types - // "extra_specs": {} - // th.AssertEquals(t, vt.ExtraSpecs, createOpts.ExtraSpecs) - - t.Logf("Successfully created volume type: %s", vt.ID) - - return vt, nil -} - -// CreateVolumeTypeNoExtraSpecs will create a volume type with a random name and -// no extra specs. This is required to bypass cinder-scheduler filters and be able -// to create a volume with this volumeType. An error will be returned if the volume -// type was unable to be created. -func CreateVolumeTypeNoExtraSpecs(t *testing.T, client *golangsdk.ServiceClient) (*volumetypes.VolumeType, error) { - name := tools.RandomString("ACPTTEST", 16) - description := "create_from_gophercloud" - t.Logf("Attempting to create volume type: %s", name) - - createOpts := volumetypes.CreateOpts{ - Name: name, - ExtraSpecs: map[string]string{}, - Description: description, - } - - vt, err := volumetypes.Create(client, createOpts) - if err != nil { - return nil, err - } - - tools.PrintResource(t, vt) - th.AssertEquals(t, vt.IsPublic, true) - th.AssertEquals(t, vt.Name, name) - th.AssertEquals(t, vt.Description, description) - - t.Logf("Successfully created volume type: %s", vt.ID) - - return vt, nil -} - -// CreatePrivateVolumeType will create a private volume type with a random -// name and no extra specs. An error will be returned if the volume type was -// unable to be created. -func CreatePrivateVolumeType(t *testing.T, client *golangsdk.ServiceClient) (*volumetypes.VolumeType, error) { - name := tools.RandomString("ACPTTEST", 16) - description := "create_from_gophercloud" - isPublic := false - t.Logf("Attempting to create volume type: %s", name) - - createOpts := volumetypes.CreateOpts{ - Name: name, - ExtraSpecs: map[string]string{}, - Description: description, - IsPublic: &isPublic, - } - - vt, err := volumetypes.Create(client, createOpts) - if err != nil { - return nil, err - } - - tools.PrintResource(t, vt) - th.AssertEquals(t, vt.IsPublic, false) - th.AssertEquals(t, vt.Name, name) - th.AssertEquals(t, vt.Description, description) - - t.Logf("Successfully created volume type: %s", vt.ID) - - return vt, nil -} - // DeleteSnapshot will delete a snapshot. A fatal error will occur if the // snapshot failed to be deleted. func DeleteSnapshot(t *testing.T, client *golangsdk.ServiceClient, snapshot *snapshots.Snapshot) { diff --git a/acceptance/openstack/evs/v3/extensions/extensions.go b/acceptance/openstack/evs/v3/extensions/extensions.go index 90d621407..2bb35c2bd 100644 --- a/acceptance/openstack/evs/v3/extensions/extensions.go +++ b/acceptance/openstack/evs/v3/extensions/extensions.go @@ -8,14 +8,13 @@ import ( "strings" "testing" - backups2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/backups" - volumeactions2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/volumeactions" - golangsdk "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/images" "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/servers" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/volumeactions" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v2/volumes" + backups2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/backups" v3 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumetypes" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" @@ -23,18 +22,18 @@ import ( // CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be // returned -func CreateUploadImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) (volumeactions2.VolumeImage, error) { +func CreateUploadImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) (volumeactions.VolumeImage, error) { if testing.Short() { t.Skip("Skipping test that requires volume-backed image uploading in short mode.") } imageName := tools.RandomString("ACPTTEST", 16) - uploadImageOpts := volumeactions2.UploadImageOpts{ + uploadImageOpts := volumeactions.UploadImageOpts{ ImageName: imageName, Force: true, } - volumeImage, err := volumeactions2.UploadImage(client, volume.ID, uploadImageOpts) + volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts) if err != nil { return volumeImage, err } @@ -75,7 +74,7 @@ func CreateVolumeAttach(t *testing.T, client *golangsdk.ServiceClient, volume *v t.Skip("Skipping test that requires volume attachment in short mode.") } - attachOpts := volumeactions2.AttachOpts{ + attachOpts := volumeactions.AttachOpts{ MountPoint: "/mnt", Mode: "rw", InstanceUUID: server.ID, @@ -83,7 +82,7 @@ func CreateVolumeAttach(t *testing.T, client *golangsdk.ServiceClient, volume *v t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) - if err := volumeactions2.Attach(client, volume.ID, attachOpts); err != nil { + if err := volumeactions.Attach(client, volume.ID, attachOpts); err != nil { return err } @@ -105,7 +104,7 @@ func CreateVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume * t.Logf("Attempting to reserve volume %s", volume.ID) - if err := volumeactions2.Reserve(client, volume.ID); err != nil { + if err := volumeactions.Reserve(client, volume.ID); err != nil { return err } @@ -120,11 +119,11 @@ func CreateVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume * func DeleteVolumeAttach(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) { t.Logf("Attepting to detach volume volume: %s", volume.ID) - detachOpts := volumeactions2.DetachOpts{ + detachOpts := volumeactions.DetachOpts{ AttachmentID: volume.Attachments[0].AttachmentID, } - if err := volumeactions2.Detach(client, volume.ID, detachOpts); err != nil { + if err := volumeactions.Detach(client, volume.ID, detachOpts); err != nil { t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) } @@ -145,7 +144,7 @@ func DeleteVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume * t.Logf("Attempting to unreserve volume %s", volume.ID) - if err := volumeactions2.Unreserve(client, volume.ID); err != nil { + if err := volumeactions.Unreserve(client, volume.ID); err != nil { t.Fatalf("Unable to unreserve volume %s: %v", volume.ID, err) } @@ -156,11 +155,11 @@ func DeleteVolumeReserve(t *testing.T, client *golangsdk.ServiceClient, volume * func ExtendVolumeSize(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { t.Logf("Attempting to extend the size of volume %s", volume.ID) - extendOpts := volumeactions2.ExtendSizeOpts{ + extendOpts := volumeactions.ExtendSizeOpts{ NewSize: 2, } - err := volumeactions2.ExtendSize(client, volume.ID, extendOpts) + err := volumeactions.ExtendSize(client, volume.ID, extendOpts) if err != nil { return err } @@ -176,13 +175,13 @@ func ExtendVolumeSize(t *testing.T, client *golangsdk.ServiceClient, volume *vol func SetImageMetadata(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { t.Logf("Attempting to apply image metadata to volume %s", volume.ID) - imageMetadataOpts := volumeactions2.ImageMetadataOpts{ + imageMetadataOpts := volumeactions.ImageMetadataOpts{ Metadata: map[string]string{ "image_name": "testimage", }, } - err := volumeactions2.SetImageMetadata(client, volume.ID, imageMetadataOpts) + err := volumeactions.SetImageMetadata(client, volume.ID, imageMetadataOpts) if err != nil { return err } @@ -255,11 +254,11 @@ func WaitForBackupStatus(client *golangsdk.ServiceClient, id, status string) err func SetBootable(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { t.Logf("Attempting to apply bootable status to volume %s", volume.ID) - bootableOpts := volumeactions2.BootableOpts{ + bootableOpts := volumeactions.BootableOpts{ Bootable: true, } - err := volumeactions2.SetBootable(client, volume.ID, bootableOpts) + err := volumeactions.SetBootable(client, volume.ID, bootableOpts) if err != nil { return err } @@ -273,11 +272,11 @@ func SetBootable(t *testing.T, client *golangsdk.ServiceClient, volume *volumes. return fmt.Errorf("Volume bootable status is %q, expected 'true'", vol.Bootable) } - bootableOpts = volumeactions2.BootableOpts{ + bootableOpts = volumeactions.BootableOpts{ Bootable: false, } - err = volumeactions2.SetBootable(client, volume.ID, bootableOpts) + err = volumeactions.SetBootable(client, volume.ID, bootableOpts) if err != nil { return err } @@ -298,12 +297,12 @@ func SetBootable(t *testing.T, client *golangsdk.ServiceClient, volume *volumes. func ChangeVolumeType(t *testing.T, client *golangsdk.ServiceClient, volume *v3.Volume, vt *volumetypes.VolumeType) error { t.Logf("Attempting to change the type of volume %s from %s to %s", volume.ID, volume.VolumeType, vt.Name) - changeOpts := volumeactions2.ChangeTypeOpts{ + changeOpts := volumeactions.ChangeTypeOpts{ NewType: vt.Name, - MigrationPolicy: volumeactions2.MigrationPolicyOnDemand, + MigrationPolicy: volumeactions.MigrationPolicyOnDemand, } - err := volumeactions2.ChangeType(client, volume.ID, changeOpts) + err := volumeactions.ChangeType(client, volume.ID, changeOpts) if err != nil { return err } @@ -319,12 +318,12 @@ func ChangeVolumeType(t *testing.T, client *golangsdk.ServiceClient, volume *v3. func ReImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume, imageID string) error { t.Logf("Attempting to re-image volume %s", volume.ID) - reimageOpts := volumeactions2.ReImageOpts{ + reimageOpts := volumeactions.ReImageOpts{ ImageID: imageID, ReImageReserved: false, } - err := volumeactions2.ReImage(client, volume.ID, reimageOpts) + err := volumeactions.ReImage(client, volume.ID, reimageOpts) if err != nil { return err } diff --git a/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go b/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go index 96a6fd16a..174906c5f 100644 --- a/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go +++ b/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go @@ -3,10 +3,9 @@ package extensions import ( "testing" - schedulerstats2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/schedulerstats" - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/schedulerstats" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) @@ -16,14 +15,14 @@ func TestSchedulerStatsList(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - listOpts := schedulerstats2.ListOpts{ + listOpts := schedulerstats.ListOpts{ Detail: true, } - allPages, err := schedulerstats2.List(blockClient, listOpts).AllPages() + allPages, err := schedulerstats.List(blockClient, listOpts).AllPages() th.AssertNoErr(t, err) - allStats, err := schedulerstats2.ExtractStoragePools(allPages) + allStats, err := schedulerstats.ExtractStoragePools(allPages) th.AssertNoErr(t, err) for _, stat := range allStats { diff --git a/acceptance/openstack/evs/v3/quotaset_test.go b/acceptance/openstack/evs/v3/quotaset_test.go index 0041defc6..7cffc04f5 100644 --- a/acceptance/openstack/evs/v3/quotaset_test.go +++ b/acceptance/openstack/evs/v3/quotaset_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - quotasets2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/quotasets" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/quotasets" golangsdk "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" @@ -18,7 +18,7 @@ func TestQuotasetGet(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSet, err := quotasets2.Get(client, projectID) + quotaSet, err := quotasets.Get(client, projectID) th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) @@ -29,7 +29,7 @@ func TestQuotasetGetDefaults(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSet, err := quotasets2.GetDefaults(client, projectID) + quotaSet, err := quotasets.GetDefaults(client, projectID) th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) @@ -40,13 +40,13 @@ func TestQuotasetGetUsage(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSetUsage, err := quotasets2.GetUsage(client, projectID) + quotaSetUsage, err := quotasets.GetUsage(client, projectID) th.AssertNoErr(t, err) tools.PrintResource(t, quotaSetUsage) } -var UpdateQuotaOpts = quotasets2.UpdateOpts{ +var UpdateQuotaOpts = quotasets.UpdateOpts{ Volumes: golangsdk.IntToPointer(100), Snapshots: golangsdk.IntToPointer(200), Gigabytes: golangsdk.IntToPointer(300), @@ -59,7 +59,7 @@ var UpdateQuotaOpts = quotasets2.UpdateOpts{ }, } -var UpdatedQuotas = quotasets2.QuotaSet{ +var UpdatedQuotas = quotasets.QuotaSet{ Volumes: 100, Snapshots: 200, Gigabytes: 300, @@ -83,7 +83,7 @@ func TestQuotasetUpdate(t *testing.T) { client, projectID := getClientAndProject(t) // save original quotas - orig, err := quotasets2.Get(client, projectID) + orig, err := quotasets.Get(client, projectID) th.AssertNoErr(t, err) // create volumeType to test volume type quota @@ -91,24 +91,24 @@ func TestQuotasetUpdate(t *testing.T) { th.AssertNoErr(t, err) defer func() { - restore := quotasets2.UpdateOpts{} + restore := quotasets.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) err := volumetypes.Delete(client, volumeType.ID) th.AssertNoErr(t, err) - _, err = quotasets2.Update(client, projectID, restore) + _, err = quotasets.Update(client, projectID, restore) th.AssertNoErr(t, err) }() // test Update - resultQuotas, err := quotasets2.Update(client, projectID, UpdateQuotaOpts) + resultQuotas, err := quotasets.Update(client, projectID, UpdateQuotaOpts) th.AssertNoErr(t, err) // We dont know the default quotas, so just check if the quotas are not the // same as before - newQuotas, err := quotasets2.Get(client, projectID) + newQuotas, err := quotasets.Get(client, projectID) th.AssertNoErr(t, err) th.AssertEquals(t, resultQuotas.Volumes, newQuotas.Volumes) th.AssertEquals(t, resultQuotas.Extra["volumes_foo"], newQuotas.Extra["volumes_foo"]) @@ -141,26 +141,26 @@ func TestQuotasetDelete(t *testing.T) { client, projectID := getClientAndProject(t) // save original quotas - orig, err := quotasets2.Get(client, projectID) + orig, err := quotasets.Get(client, projectID) th.AssertNoErr(t, err) defer func() { - restore := quotasets2.UpdateOpts{} + restore := quotasets.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) - _, err = quotasets2.Update(client, projectID, restore) + _, err = quotasets.Update(client, projectID, restore) th.AssertNoErr(t, err) }() // Obtain environment default quotaset values to validate deletion. - defaultQuotaSet, err := quotasets2.GetDefaults(client, projectID) + defaultQuotaSet, err := quotasets.GetDefaults(client, projectID) th.AssertNoErr(t, err) // Test Delete - err = quotasets2.Delete(client, projectID) + err = quotasets.Delete(client, projectID) th.AssertNoErr(t, err) - newQuotas, err := quotasets2.Get(client, projectID) + newQuotas, err := quotasets.Get(client, projectID) th.AssertNoErr(t, err) th.AssertEquals(t, newQuotas.Volumes, defaultQuotaSet.Volumes) @@ -177,7 +177,7 @@ func getClientAndProject(t *testing.T) (*golangsdk.ServiceClient, string) { return client, projectID } -func FillUpdateOptsFromQuotaSet(src quotasets2.QuotaSet, dest *quotasets2.UpdateOpts) { +func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) { dest.Volumes = &src.Volumes dest.Snapshots = &src.Snapshots dest.Gigabytes = &src.Gigabytes diff --git a/acceptance/openstack/evs/v3/volumetypes_test.go b/acceptance/openstack/evs/v3/volumetypes_test.go deleted file mode 100644 index a9763eca6..000000000 --- a/acceptance/openstack/evs/v3/volumetypes_test.go +++ /dev/null @@ -1,162 +0,0 @@ -package v3 - -import ( - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" - identity "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/identity/v3" - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumetypes" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" -) - -func TestVolumeTypes(t *testing.T) { - clients.RequireAdmin(t) - - client, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - vt, err := CreateVolumeType(t, client) - th.AssertNoErr(t, err) - defer DeleteVolumeType(t, client, vt) - - allPages, err := volumetypes.List(client, nil).AllPages() - th.AssertNoErr(t, err) - - allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, v := range allVolumeTypes { - tools.PrintResource(t, v) - if v.ID == vt.ID { - found = true - } - } - - th.AssertEquals(t, found, true) - - isPublic := false - name := vt.Name + "-UPDATED" - description := "" - updateOpts := volumetypes.UpdateOpts{ - Name: &name, - Description: &description, - IsPublic: &isPublic, - } - - newVT, err := volumetypes.Update(client, vt.ID, updateOpts) - th.AssertNoErr(t, err) - - tools.PrintResource(t, newVT) - th.AssertEquals(t, name, newVT.Name) - th.AssertEquals(t, description, newVT.Description) - th.AssertEquals(t, isPublic, newVT.IsPublic) -} - -func TestVolumeTypesExtraSpecs(t *testing.T) { - clients.RequireAdmin(t) - - client, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - vt, err := CreateVolumeTypeNoExtraSpecs(t, client) - th.AssertNoErr(t, err) - defer DeleteVolumeType(t, client, vt) - - createOpts := volumetypes.ExtraSpecsOpts{ - "capabilities": "gpu", - "volume_backend_name": "ssd", - } - - createdExtraSpecs, err := volumetypes.CreateExtraSpecs(client, vt.ID, createOpts) - th.AssertNoErr(t, err) - - tools.PrintResource(t, createdExtraSpecs) - - th.AssertEquals(t, len(createdExtraSpecs), 2) - th.AssertEquals(t, createdExtraSpecs["capabilities"], "gpu") - th.AssertEquals(t, createdExtraSpecs["volume_backend_name"], "ssd") - - err = volumetypes.DeleteExtraSpec(client, vt.ID, "volume_backend_name") - th.AssertNoErr(t, err) - - updateOpts := volumetypes.ExtraSpecsOpts{ - "capabilities": "gpu-2", - } - updatedExtraSpec, err := volumetypes.UpdateExtraSpec(client, vt.ID, updateOpts) - th.AssertNoErr(t, err) - - tools.PrintResource(t, updatedExtraSpec) - - th.AssertEquals(t, updatedExtraSpec["capabilities"], "gpu-2") - - allExtraSpecs, err := volumetypes.ListExtraSpecs(client, vt.ID) - th.AssertNoErr(t, err) - - tools.PrintResource(t, allExtraSpecs) - - th.AssertEquals(t, len(allExtraSpecs), 1) - th.AssertEquals(t, allExtraSpecs["capabilities"], "gpu-2") - - singleSpec, err := volumetypes.GetExtraSpec(client, vt.ID, "capabilities") - th.AssertNoErr(t, err) - - tools.PrintResource(t, singleSpec) - - th.AssertEquals(t, singleSpec["capabilities"], "gpu-2") -} - -func TestVolumeTypesAccess(t *testing.T) { - clients.RequireAdmin(t) - - client, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - identityClient, err := clients.NewIdentityV3Client() - th.AssertNoErr(t, err) - - vt, err := CreatePrivateVolumeType(t, client) - th.AssertNoErr(t, err) - defer DeleteVolumeType(t, client, vt) - - project, err := identity.CreateProject(t, identityClient, nil) - th.AssertNoErr(t, err) - defer identity.DeleteProject(t, identityClient, project.ID) - - addAccessOpts := volumetypes.AddAccessOpts{ - Project: project.ID, - } - - err = volumetypes.AddAccess(client, vt.ID, addAccessOpts) - th.AssertNoErr(t, err) - - allPages, err := volumetypes.ListAccesses(client, vt.ID).AllPages() - th.AssertNoErr(t, err) - - accessList, err := volumetypes.ExtractAccesses(allPages) - th.AssertNoErr(t, err) - - tools.PrintResource(t, accessList) - - th.AssertEquals(t, len(accessList), 1) - th.AssertEquals(t, accessList[0].ProjectID, project.ID) - th.AssertEquals(t, accessList[0].VolumeTypeID, vt.ID) - - removeAccessOpts := volumetypes.RemoveAccessOpts{ - Project: project.ID, - } - - err = volumetypes.RemoveAccess(client, vt.ID, removeAccessOpts) - th.AssertNoErr(t, err) - - allPages, err = volumetypes.ListAccesses(client, vt.ID).AllPages() - th.AssertNoErr(t, err) - - accessList, err = volumetypes.ExtractAccesses(allPages) - th.AssertNoErr(t, err) - - tools.PrintResource(t, accessList) - - th.AssertEquals(t, len(accessList), 0) -} diff --git a/openstack/evs/v3/extensions/availabilityzones/doc.go b/openstack/evs/extensions/availabilityzones/doc.go similarity index 100% rename from openstack/evs/v3/extensions/availabilityzones/doc.go rename to openstack/evs/extensions/availabilityzones/doc.go diff --git a/openstack/evs/v3/extensions/availabilityzones/requests.go b/openstack/evs/extensions/availabilityzones/requests.go similarity index 100% rename from openstack/evs/v3/extensions/availabilityzones/requests.go rename to openstack/evs/extensions/availabilityzones/requests.go diff --git a/openstack/evs/v3/extensions/availabilityzones/results.go b/openstack/evs/extensions/availabilityzones/results.go similarity index 100% rename from openstack/evs/v3/extensions/availabilityzones/results.go rename to openstack/evs/extensions/availabilityzones/results.go diff --git a/openstack/evs/v3/extensions/availabilityzones/urls.go b/openstack/evs/extensions/availabilityzones/urls.go similarity index 100% rename from openstack/evs/v3/extensions/availabilityzones/urls.go rename to openstack/evs/extensions/availabilityzones/urls.go diff --git a/openstack/evs/v3/extensions/backups/doc.go b/openstack/evs/extensions/backups/doc.go similarity index 100% rename from openstack/evs/v3/extensions/backups/doc.go rename to openstack/evs/extensions/backups/doc.go diff --git a/openstack/evs/v3/extensions/backups/requests.go b/openstack/evs/extensions/backups/requests.go similarity index 100% rename from openstack/evs/v3/extensions/backups/requests.go rename to openstack/evs/extensions/backups/requests.go diff --git a/openstack/evs/v3/extensions/backups/results.go b/openstack/evs/extensions/backups/results.go similarity index 100% rename from openstack/evs/v3/extensions/backups/results.go rename to openstack/evs/extensions/backups/results.go diff --git a/openstack/evs/v3/extensions/backups/urls.go b/openstack/evs/extensions/backups/urls.go similarity index 100% rename from openstack/evs/v3/extensions/backups/urls.go rename to openstack/evs/extensions/backups/urls.go diff --git a/openstack/evs/v3/extensions/limits/doc.go b/openstack/evs/extensions/limits/doc.go similarity index 100% rename from openstack/evs/v3/extensions/limits/doc.go rename to openstack/evs/extensions/limits/doc.go diff --git a/openstack/evs/v3/extensions/limits/requests.go b/openstack/evs/extensions/limits/requests.go similarity index 100% rename from openstack/evs/v3/extensions/limits/requests.go rename to openstack/evs/extensions/limits/requests.go diff --git a/openstack/evs/v3/extensions/limits/results.go b/openstack/evs/extensions/limits/results.go similarity index 100% rename from openstack/evs/v3/extensions/limits/results.go rename to openstack/evs/extensions/limits/results.go diff --git a/openstack/evs/v3/extensions/limits/urls.go b/openstack/evs/extensions/limits/urls.go similarity index 100% rename from openstack/evs/v3/extensions/limits/urls.go rename to openstack/evs/extensions/limits/urls.go diff --git a/openstack/evs/v3/extensions/quotasets/requests.go b/openstack/evs/extensions/quotasets/requests.go similarity index 100% rename from openstack/evs/v3/extensions/quotasets/requests.go rename to openstack/evs/extensions/quotasets/requests.go diff --git a/openstack/evs/v3/extensions/quotasets/results.go b/openstack/evs/extensions/quotasets/results.go similarity index 100% rename from openstack/evs/v3/extensions/quotasets/results.go rename to openstack/evs/extensions/quotasets/results.go diff --git a/openstack/evs/v3/extensions/quotasets/urls.go b/openstack/evs/extensions/quotasets/urls.go similarity index 100% rename from openstack/evs/v3/extensions/quotasets/urls.go rename to openstack/evs/extensions/quotasets/urls.go diff --git a/openstack/evs/v3/extensions/schedulerhints/doc.go b/openstack/evs/extensions/schedulerhints/doc.go similarity index 100% rename from openstack/evs/v3/extensions/schedulerhints/doc.go rename to openstack/evs/extensions/schedulerhints/doc.go diff --git a/openstack/evs/v3/extensions/schedulerhints/requests.go b/openstack/evs/extensions/schedulerhints/requests.go similarity index 100% rename from openstack/evs/v3/extensions/schedulerhints/requests.go rename to openstack/evs/extensions/schedulerhints/requests.go diff --git a/openstack/evs/v3/extensions/schedulerstats/requests.go b/openstack/evs/extensions/schedulerstats/requests.go similarity index 100% rename from openstack/evs/v3/extensions/schedulerstats/requests.go rename to openstack/evs/extensions/schedulerstats/requests.go diff --git a/openstack/evs/v3/extensions/schedulerstats/results.go b/openstack/evs/extensions/schedulerstats/results.go similarity index 100% rename from openstack/evs/v3/extensions/schedulerstats/results.go rename to openstack/evs/extensions/schedulerstats/results.go diff --git a/openstack/evs/v3/extensions/schedulerstats/urls.go b/openstack/evs/extensions/schedulerstats/urls.go similarity index 100% rename from openstack/evs/v3/extensions/schedulerstats/urls.go rename to openstack/evs/extensions/schedulerstats/urls.go diff --git a/openstack/evs/v3/extensions/volumeactions/requests.go b/openstack/evs/extensions/volumeactions/requests.go similarity index 100% rename from openstack/evs/v3/extensions/volumeactions/requests.go rename to openstack/evs/extensions/volumeactions/requests.go diff --git a/openstack/evs/v3/extensions/volumeactions/results.go b/openstack/evs/extensions/volumeactions/results.go similarity index 100% rename from openstack/evs/v3/extensions/volumeactions/results.go rename to openstack/evs/extensions/volumeactions/results.go diff --git a/openstack/evs/v3/extensions/volumeactions/urls.go b/openstack/evs/extensions/volumeactions/urls.go similarity index 100% rename from openstack/evs/v3/extensions/volumeactions/urls.go rename to openstack/evs/extensions/volumeactions/urls.go diff --git a/openstack/evs/v3/extensions/volumehost/doc.go b/openstack/evs/extensions/volumehost/doc.go similarity index 100% rename from openstack/evs/v3/extensions/volumehost/doc.go rename to openstack/evs/extensions/volumehost/doc.go diff --git a/openstack/evs/v3/extensions/volumehost/results.go b/openstack/evs/extensions/volumehost/results.go similarity index 100% rename from openstack/evs/v3/extensions/volumehost/results.go rename to openstack/evs/extensions/volumehost/results.go diff --git a/openstack/evs/v3/extensions/volumetenants/doc.go b/openstack/evs/extensions/volumetenants/doc.go similarity index 100% rename from openstack/evs/v3/extensions/volumetenants/doc.go rename to openstack/evs/extensions/volumetenants/doc.go diff --git a/openstack/evs/v3/extensions/volumetenants/results.go b/openstack/evs/extensions/volumetenants/results.go similarity index 100% rename from openstack/evs/v3/extensions/volumetenants/results.go rename to openstack/evs/extensions/volumetenants/results.go diff --git a/openstack/evs/v3/extensions/volumetransfers/doc.go b/openstack/evs/extensions/volumetransfers/doc.go similarity index 100% rename from openstack/evs/v3/extensions/volumetransfers/doc.go rename to openstack/evs/extensions/volumetransfers/doc.go diff --git a/openstack/evs/v3/extensions/volumetransfers/requests.go b/openstack/evs/extensions/volumetransfers/requests.go similarity index 100% rename from openstack/evs/v3/extensions/volumetransfers/requests.go rename to openstack/evs/extensions/volumetransfers/requests.go diff --git a/openstack/evs/v3/extensions/volumetransfers/results.go b/openstack/evs/extensions/volumetransfers/results.go similarity index 100% rename from openstack/evs/v3/extensions/volumetransfers/results.go rename to openstack/evs/extensions/volumetransfers/results.go diff --git a/openstack/evs/v3/extensions/volumetransfers/urls.go b/openstack/evs/extensions/volumetransfers/urls.go similarity index 100% rename from openstack/evs/v3/extensions/volumetransfers/urls.go rename to openstack/evs/extensions/volumetransfers/urls.go diff --git a/openstack/evs/v3/extensions/quotasets/doc.go b/openstack/evs/v3/extensions/quotasets/doc.go deleted file mode 100644 index fc3912239..000000000 --- a/openstack/evs/v3/extensions/quotasets/doc.go +++ /dev/null @@ -1,60 +0,0 @@ -/* -Package quotasets enables retrieving and managing Block Storage quotas. - -Example to Get a Quota Set - - quotaset, err := quotasets.Get(blockStorageClient, "project-id").Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", quotaset) - -Example to Get Quota Set Usage - - quotaset, err := quotasets.GetUsage(blockStorageClient, "project-id").Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", quotaset) - -Example to Update a Quota Set - - updateOpts := quotasets.UpdateOpts{ - Volumes: golangsdk.IntToPointer(100), - } - - quotaset, err := quotasets.Update(blockStorageClient, "project-id", updateOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", quotaset) - -Example to Update a Quota set with volume_type quotas - - updateOpts := quotasets.UpdateOpts{ - Volumes: golangsdk.IntToPointer(100), - Extra: map[string]interface{}{ - "gigabytes_foo": golangsdk.IntToPointer(100), - "snapshots_foo": golangsdk.IntToPointer(10), - "volumes_foo": golangsdk.IntToPointer(10), - }, - } - - quotaset, err := quotasets.Update(blockStorageClient, "project-id", updateOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", quotaset) - -Example to Delete a Quota Set - - err := quotasets.Delete(blockStorageClient, "project-id").ExtractErr() - if err != nil { - panic(err) - } -*/ -package quotasets diff --git a/openstack/evs/v3/extensions/schedulerstats/doc.go b/openstack/evs/v3/extensions/schedulerstats/doc.go deleted file mode 100644 index b0a2c8ff3..000000000 --- a/openstack/evs/v3/extensions/schedulerstats/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Package schedulerstats returns information about block storage pool capacity -and utilisation. Example: - - listOpts := schedulerstats.ListOpts{ - Detail: true, - } - - allPages, err := schedulerstats.List(client, listOpts).AllPages() - if err != nil { - panic(err) - } - - allStats, err := schedulerstats.ExtractStoragePools(allPages) - if err != nil { - panic(err) - } - - for _, stat := range allStats { - fmt.Printf("%+v\n", stat) - } -*/ -package schedulerstats diff --git a/openstack/evs/v3/extensions/services/doc.go b/openstack/evs/v3/extensions/services/doc.go deleted file mode 100644 index b3fba4cd6..000000000 --- a/openstack/evs/v3/extensions/services/doc.go +++ /dev/null @@ -1,22 +0,0 @@ -/* -Package services returns information about the blockstorage services in the -OpenStack cloud. - -Example of Retrieving list of all services - - allPages, err := services.List(blockstorageClient, services.ListOpts{}).AllPages() - if err != nil { - panic(err) - } - - allServices, err := services.ExtractServices(allPages) - if err != nil { - panic(err) - } - - for _, service := range allServices { - fmt.Printf("%+v\n", service) - } -*/ - -package services diff --git a/openstack/evs/v3/extensions/services/requests.go b/openstack/evs/v3/extensions/services/requests.go deleted file mode 100644 index 84c7de2a2..000000000 --- a/openstack/evs/v3/extensions/services/requests.go +++ /dev/null @@ -1,42 +0,0 @@ -package services - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToServiceListQuery() (string, error) -} - -// ListOpts holds options for listing Services. -type ListOpts struct { - // Filter the service list result by binary name of the service. - Binary string `q:"binary"` - - // Filter the service list result by host name of the service. - Host string `q:"host"` -} - -// ToServiceListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToServiceListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// List makes a request against the API to list services. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToServiceListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ServicePage{pagination.SinglePageBase(r)} - }) -} diff --git a/openstack/evs/v3/extensions/services/results.go b/openstack/evs/v3/extensions/services/results.go deleted file mode 100644 index 338a257cf..000000000 --- a/openstack/evs/v3/extensions/services/results.go +++ /dev/null @@ -1,84 +0,0 @@ -package services - -import ( - "encoding/json" - "time" - - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// Service represents a Blockstorage service in the OpenStack cloud. -type Service struct { - // The binary name of the service. - Binary string `json:"binary"` - - // The reason for disabling a service. - DisabledReason string `json:"disabled_reason"` - - // The name of the host. - Host string `json:"host"` - - // The state of the service. One of up or down. - State string `json:"state"` - - // The status of the service. One of available or unavailable. - Status string `json:"status"` - - // The date and time stamp when the extension was last updated. - UpdatedAt time.Time `json:"-"` - - // The availability zone name. - Zone string `json:"zone"` - - // The following fields are optional - - // The host is frozen or not. Only in cinder-volume service. - Frozen bool `json:"frozen"` - - // The cluster name. Only in cinder-volume service. - Cluster string `json:"cluster"` - - // The volume service replication status. Only in cinder-volume service. - ReplicationStatus string `json:"replication_status"` - - // The ID of active storage backend. Only in cinder-volume service. - ActiveBackendID string `json:"active_backend_id"` -} - -// UnmarshalJSON to override default -func (r *Service) UnmarshalJSON(b []byte) error { - type tmp Service - var s struct { - tmp - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Service(s.tmp) - - r.UpdatedAt = time.Time(s.UpdatedAt) - - return nil -} - -// ServicePage represents a single page of all Services from a List request. -type ServicePage struct { - pagination.SinglePageBase -} - -// IsEmpty determines whether or not a page of Services contains any results. -func (page ServicePage) IsEmpty() (bool, error) { - services, err := ExtractServices(page) - return len(services) == 0, err -} - -func ExtractServices(r pagination.Page) ([]Service, error) { - var s struct { - Service []Service `json:"services"` - } - err := (r.(ServicePage)).ExtractInto(&s) - return s.Service, err -} diff --git a/openstack/evs/v3/extensions/services/urls.go b/openstack/evs/v3/extensions/services/urls.go deleted file mode 100644 index a718789cd..000000000 --- a/openstack/evs/v3/extensions/services/urls.go +++ /dev/null @@ -1,7 +0,0 @@ -package services - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("os-services") -} diff --git a/openstack/evs/v3/extensions/volumeactions/doc.go b/openstack/evs/v3/extensions/volumeactions/doc.go deleted file mode 100644 index c83b0997a..000000000 --- a/openstack/evs/v3/extensions/volumeactions/doc.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Package volumeactions provides information and interaction with volumes in the -OpenStack Block Storage service. A volume is a detachable block storage -device, akin to a USB hard drive. - -Example of Attaching a Volume to an Instance - - attachOpts := volumeactions.AttachOpts{ - MountPoint: "/mnt", - Mode: "rw", - InstanceUUID: server.ID, - } - - err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr() - if err != nil { - panic(err) - } - - detachOpts := volumeactions.DetachOpts{ - AttachmentID: volume.Attachments[0].AttachmentID, - } - - err = volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example of Creating an Image from a Volume - - uploadImageOpts := volumeactions.UploadImageOpts{ - ImageName: "my_vol", - Force: true, - } - - volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", volumeImage) - -Example of Extending a Volume's Size - - extendOpts := volumeactions.ExtendSizeOpts{ - NewSize: 100, - } - - err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example of Initializing a Volume Connection - - connectOpts := &volumeactions.InitializeConnectionOpts{ - IP: "127.0.0.1", - Host: "stack", - Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: golangsdk.Disabled, - Platform: "x86_64", - OSType: "linux2", - } - - connectionInfo, err := volumeactions.InitializeConnection(client, volume.ID, connectOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", connectionInfo["data"]) - - terminateOpts := &volumeactions.InitializeConnectionOpts{ - IP: "127.0.0.1", - Host: "stack", - Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: golangsdk.Disabled, - Platform: "x86_64", - OSType: "linux2", - } - - err = volumeactions.TerminateConnection(client, volume.ID, terminateOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example of Setting a Volume's Bootable status - - options := volumeactions.BootableOpts{ - Bootable: true, - } - - err := volumeactions.SetBootable(client, volume.ID, options).ExtractErr() - if err != nil { - panic(err) - } - -Example of Changing Type of a Volume - - changeTypeOpts := volumeactions.ChangeTypeOpts{ - NewType: "ssd", - MigrationPolicy: volumeactions.MigrationPolicyOnDemand, - } - - err = volumeactions.ChangeType(client, volumeID, changeTypeOpts).ExtractErr() - if err != nil { - panic(err) - } -*/ -package volumeactions diff --git a/openstack/evs/v3/volumetypes/List.go b/openstack/evs/v3/volumetypes/List.go index fe43952e2..fcb203151 100644 --- a/openstack/evs/v3/volumetypes/List.go +++ b/openstack/evs/v3/volumetypes/List.go @@ -64,3 +64,10 @@ func (r VolumeTypePage) NextPageURL() (string, error) { func ExtractVolumeTypesInto(r pagination.Page, v interface{}) error { return extract.IntoSlicePtr(r.(VolumeTypePage).BodyReader(), v, "volume_types") } + +// ExtractVolumeTypes extracts and returns Volumes. It is used while iterating over a volumetypes.List call. +func ExtractVolumeTypes(r pagination.Page) ([]VolumeType, error) { + var s []VolumeType + err := ExtractVolumeTypesInto(r, &s) + return s, err +} From 5dea94cc617468818ae6d8bd565b815e95646e09 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 15:22:39 +0100 Subject: [PATCH 19/51] os-quota-sets --- .../evs/extensions/quotasets/GetDefaults.go | 17 ++ openstack/evs/extensions/quotasets/get.go | 11 - .../evs/extensions/quotasets/requests.go | 116 ---------- openstack/evs/extensions/quotasets/results.go | 205 ------------------ .../extensions/quotasets/testing/fixtures.go | 163 -------------- .../quotasets/testing/requests_test.go | 63 ------ openstack/evs/extensions/quotasets/update.go | 3 +- openstack/evs/extensions/quotasets/urls.go | 21 -- 8 files changed, 19 insertions(+), 580 deletions(-) create mode 100644 openstack/evs/extensions/quotasets/GetDefaults.go delete mode 100644 openstack/evs/extensions/quotasets/requests.go delete mode 100644 openstack/evs/extensions/quotasets/results.go delete mode 100644 openstack/evs/extensions/quotasets/testing/fixtures.go delete mode 100644 openstack/evs/extensions/quotasets/testing/requests_test.go delete mode 100644 openstack/evs/extensions/quotasets/urls.go diff --git a/openstack/evs/extensions/quotasets/GetDefaults.go b/openstack/evs/extensions/quotasets/GetDefaults.go new file mode 100644 index 000000000..9da7bcc46 --- /dev/null +++ b/openstack/evs/extensions/quotasets/GetDefaults.go @@ -0,0 +1,17 @@ +package quotasets + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +func GetDefaults(client *golangsdk.ServiceClient, projectID string) (*QuotaSet, error) { + raw, err := client.Get(client.ServiceURL("os-quota-sets", projectID, "defaults"), nil, nil) + if err != nil { + return nil, err + } + + var res QuotaSet + err = extract.IntoStructPtr(raw.Body, &res, "quota_set") + return &res, err +} diff --git a/openstack/evs/extensions/quotasets/get.go b/openstack/evs/extensions/quotasets/get.go index e95b03b7b..8bb192174 100644 --- a/openstack/evs/extensions/quotasets/get.go +++ b/openstack/evs/extensions/quotasets/get.go @@ -18,17 +18,6 @@ func Get(client *golangsdk.ServiceClient, projectID string) (*QuotaSet, error) { return &res.QuotaSet, err } -func GetDefaults(client *golangsdk.ServiceClient, projectID string) (*QuotaSet, error) { - raw, err := client.Get(client.ServiceURL("os-quota-sets", projectID, "defaults"), nil, nil) - if err != nil { - return nil, err - } - - var res QuotaSet - err = extract.IntoStructPtr(raw.Body, &res, "quota_set") - return &res, err -} - type QuotaSet struct { // ID is project associated with this QuotaSet. ID string `json:"id"` diff --git a/openstack/evs/extensions/quotasets/requests.go b/openstack/evs/extensions/quotasets/requests.go deleted file mode 100644 index b4209933e..000000000 --- a/openstack/evs/extensions/quotasets/requests.go +++ /dev/null @@ -1,116 +0,0 @@ -package quotasets - -import ( - "fmt" - - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// Get returns public data about a previously created QuotaSet. -func Get(client *golangsdk.ServiceClient, projectID string) (r GetResult) { - resp, err := client.Get(getURL(client, projectID), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// GetDefaults returns public data about the project's default block storage quotas. -func GetDefaults(client *golangsdk.ServiceClient, projectID string) (r GetResult) { - resp, err := client.Get(getDefaultsURL(client, projectID), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// GetUsage returns detailed public data about a previously created QuotaSet. -func GetUsage(client *golangsdk.ServiceClient, projectID string) (r GetUsageResult) { - u := fmt.Sprintf("%s?usage=true", getURL(client, projectID)) - resp, err := client.Get(u, &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Updates the quotas for the given projectID and returns the new QuotaSet. -func Update(client *golangsdk.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToBlockStorageQuotaUpdateMap() - if err != nil { - r.Err = err - return - } - - resp, err := client.Put(updateURL(client, projectID), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder enables extensions to add parameters to the update request. -type UpdateOptsBuilder interface { - // Extra specific name to prevent collisions with interfaces for other quotas - // (e.g. neutron) - ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) -} - -// ToBlockStorageQuotaUpdateMap builds the update options into a serializable -// format. -func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { - b, err := golangsdk.BuildRequestBody(opts, "quota_set") - if err != nil { - return nil, err - } - - if opts.Extra != nil { - if v, ok := b["quota_set"].(map[string]interface{}); ok { - for key, value := range opts.Extra { - v[key] = value - } - } - } - - return b, nil -} - -// Options for Updating the quotas of a Tenant. -// All int-values are pointers so they can be nil if they are not needed. -// You can use gopercloud.IntToPointer() for convenience -type UpdateOpts struct { - // Volumes is the number of volumes that are allowed for each project. - Volumes *int `json:"volumes,omitempty"` - - // Snapshots is the number of snapshots that are allowed for each project. - Snapshots *int `json:"snapshots,omitempty"` - - // Gigabytes is the size (GB) of volumes and snapshots that are allowed for - // each project. - Gigabytes *int `json:"gigabytes,omitempty"` - - // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are - // allowed for each project and the specifed volume type. - PerVolumeGigabytes *int `json:"per_volume_gigabytes,omitempty"` - - // Backups is the number of backups that are allowed for each project. - Backups *int `json:"backups,omitempty"` - - // BackupGigabytes is the size (GB) of backups that are allowed for each - // project. - BackupGigabytes *int `json:"backup_gigabytes,omitempty"` - - // Groups is the number of groups that are allowed for each project. - Groups *int `json:"groups,omitempty"` - - // Force will update the quotaset even if the quota has already been used - // and the reserved quota exceeds the new quota. - Force bool `json:"force,omitempty"` - - // Extra is a collection of miscellaneous key/values used to set - // quota per volume_type - Extra map[string]interface{} `json:"-"` -} - -// Resets the quotas for the given tenant to their default values. -func Delete(client *golangsdk.ServiceClient, projectID string) (r DeleteResult) { - resp, err := client.Delete(updateURL(client, projectID), &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/extensions/quotasets/results.go b/openstack/evs/extensions/quotasets/results.go deleted file mode 100644 index 2849d4c05..000000000 --- a/openstack/evs/extensions/quotasets/results.go +++ /dev/null @@ -1,205 +0,0 @@ -package quotasets - -import ( - "encoding/json" - - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// QuotaSet is a set of operational limits that allow for control of block -// storage usage. -type QuotaSet struct { - // ID is project associated with this QuotaSet. - ID string `json:"id"` - - // Volumes is the number of volumes that are allowed for each project. - Volumes int `json:"volumes"` - - // Snapshots is the number of snapshots that are allowed for each project. - Snapshots int `json:"snapshots"` - - // Gigabytes is the size (GB) of volumes and snapshots that are allowed for - // each project. - Gigabytes int `json:"gigabytes"` - - // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are - // allowed for each project and the specifed volume type. - PerVolumeGigabytes int `json:"per_volume_gigabytes"` - - // Backups is the number of backups that are allowed for each project. - Backups int `json:"backups"` - - // BackupGigabytes is the size (GB) of backups that are allowed for each - // project. - BackupGigabytes int `json:"backup_gigabytes"` - - // Groups is the number of groups that are allowed for each project. - Groups int `json:"groups,omitempty"` - - // Extra is a collection of miscellaneous key/values used to set - // quota per volume_type - Extra map[string]interface{} `json:"-"` -} - -// UnmarshalJSON is used on QuotaSet to unmarshal extra keys that are -// used for volume_type quota -func (r *QuotaSet) UnmarshalJSON(b []byte) error { - type tmp QuotaSet - var s struct { - tmp - Extra map[string]interface{} `json:"extra"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = QuotaSet(s.tmp) - - var result interface{} - err = json.Unmarshal(b, &result) - if err != nil { - return err - } - if resultMap, ok := result.(map[string]interface{}); ok { - r.Extra = golangsdk.RemainingKeys(QuotaSet{}, resultMap) - } - - return err -} - -// QuotaUsageSet represents details of both operational limits of block -// storage resources and the current usage of those resources. -type QuotaUsageSet struct { - // ID is the project ID associated with this QuotaUsageSet. - ID string `json:"id"` - - // Volumes is the volume usage information for this project, including - // in_use, limit, reserved and allocated attributes. Note: allocated - // attribute is available only when nested quota is enabled. - Volumes QuotaUsage `json:"volumes"` - - // Snapshots is the snapshot usage information for this project, including - // in_use, limit, reserved and allocated attributes. Note: allocated - // attribute is available only when nested quota is enabled. - Snapshots QuotaUsage `json:"snapshots"` - - // Gigabytes is the size (GB) usage information of volumes and snapshots - // for this project, including in_use, limit, reserved and allocated - // attributes. Note: allocated attribute is available only when nested - // quota is enabled. - Gigabytes QuotaUsage `json:"gigabytes"` - - // PerVolumeGigabytes is the size (GB) usage information for each volume, - // including in_use, limit, reserved and allocated attributes. Note: - // allocated attribute is available only when nested quota is enabled and - // only limit is meaningful here. - PerVolumeGigabytes QuotaUsage `json:"per_volume_gigabytes"` - - // Backups is the backup usage information for this project, including - // in_use, limit, reserved and allocated attributes. Note: allocated - // attribute is available only when nested quota is enabled. - Backups QuotaUsage `json:"backups"` - - // BackupGigabytes is the size (GB) usage information of backup for this - // project, including in_use, limit, reserved and allocated attributes. - // Note: allocated attribute is available only when nested quota is - // enabled. - BackupGigabytes QuotaUsage `json:"backup_gigabytes"` - - // Groups is the number of groups that are allowed for each project. - // Note: allocated attribute is available only when nested quota is - // enabled. - Groups QuotaUsage `json:"groups"` -} - -// QuotaUsage is a set of details about a single operational limit that allows -// for control of block storage usage. -type QuotaUsage struct { - // InUse is the current number of provisioned resources of the given type. - InUse int `json:"in_use"` - - // Allocated is the current number of resources of a given type allocated - // for use. It is only available when nested quota is enabled. - Allocated int `json:"allocated"` - - // Reserved is a transitional state when a claim against quota has been made - // but the resource is not yet fully online. - Reserved int `json:"reserved"` - - // Limit is the maximum number of a given resource that can be - // allocated/provisioned. This is what "quota" usually refers to. - Limit int `json:"limit"` -} - -// QuotaSetPage stores a single page of all QuotaSet results from a List call. -type QuotaSetPage struct { - pagination.SinglePageBase -} - -// IsEmpty determines whether or not a QuotaSetsetPage is empty. -func (r QuotaSetPage) IsEmpty() (bool, error) { - ks, err := ExtractQuotaSets(r) - return len(ks) == 0, err -} - -// ExtractQuotaSets interprets a page of results as a slice of QuotaSets. -func ExtractQuotaSets(r pagination.Page) ([]QuotaSet, error) { - var s struct { - QuotaSets []QuotaSet `json:"quotas"` - } - err := (r.(QuotaSetPage)).ExtractInto(&s) - return s.QuotaSets, err -} - -type quotaResult struct { - golangsdk.Result -} - -// Extract is a method that attempts to interpret any QuotaSet resource response -// as a QuotaSet struct. -func (r quotaResult) Extract() (*QuotaSet, error) { - var s struct { - QuotaSet *QuotaSet `json:"quota_set"` - } - err := r.ExtractInto(&s) - return s.QuotaSet, err -} - -// GetResult is the response from a Get operation. Call its Extract method to -// interpret it as a QuotaSet. -type GetResult struct { - quotaResult -} - -// UpdateResult is the response from a Update operation. Call its Extract method -// to interpret it as a QuotaSet. -type UpdateResult struct { - quotaResult -} - -type quotaUsageResult struct { - golangsdk.Result -} - -// GetUsageResult is the response from a Get operation. Call its Extract -// method to interpret it as a QuotaSet. -type GetUsageResult struct { - quotaUsageResult -} - -// Extract is a method that attempts to interpret any QuotaUsageSet resource -// response as a set of QuotaUsageSet structs. -func (r quotaUsageResult) Extract() (QuotaUsageSet, error) { - var s struct { - QuotaUsageSet QuotaUsageSet `json:"quota_set"` - } - err := r.ExtractInto(&s) - return s.QuotaUsageSet, err -} - -// DeleteResult is the response from a Delete operation. Call its ExtractErr -// method to determine if the request succeeded or failed. -type DeleteResult struct { - golangsdk.ErrResult -} diff --git a/openstack/evs/extensions/quotasets/testing/fixtures.go b/openstack/evs/extensions/quotasets/testing/fixtures.go deleted file mode 100644 index e7185bb8b..000000000 --- a/openstack/evs/extensions/quotasets/testing/fixtures.go +++ /dev/null @@ -1,163 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/quotasets" - - "github.com/opentelekomcloud/gophertelekomcloud" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -const FirstTenantID = "555544443333222211110000ffffeeee" - -var getExpectedJSONBody = ` -{ - "quota_set" : { - "volumes" : 8, - "snapshots" : 9, - "gigabytes" : 10, - "per_volume_gigabytes" : 11, - "backups" : 12, - "backup_gigabytes" : 13 - } -}` - -var getExpectedQuotaSet = quotasets.QuotaSet{ - Volumes: 8, - Snapshots: 9, - Gigabytes: 10, - PerVolumeGigabytes: 11, - Backups: 12, - BackupGigabytes: 13, -} - -var getUsageExpectedJSONBody = ` -{ - "quota_set": { - "id": "555544443333222211110000ffffeeee", - "volumes": { - "in_use": 15, - "limit": 16, - "reserved": 17 - }, - "snapshots": { - "in_use": 18, - "limit": 19, - "reserved": 20 - }, - "gigabytes": { - "in_use": 21, - "limit": 22, - "reserved": 23 - }, - "per_volume_gigabytes": { - "in_use": 24, - "limit": 25, - "reserved": 26 - }, - "backups": { - "in_use": 27, - "limit": 28, - "reserved": 29 - }, - "backup_gigabytes": { - "in_use": 30, - "limit": 31, - "reserved": 32 - } - } -} -` - -var getUsageExpectedQuotaSet = quotasets.QuotaUsageSet{ - ID: FirstTenantID, - Volumes: quotasets.QuotaUsage{InUse: 15, Limit: 16, Reserved: 17}, - Snapshots: quotasets.QuotaUsage{InUse: 18, Limit: 19, Reserved: 20}, - Gigabytes: quotasets.QuotaUsage{InUse: 21, Limit: 22, Reserved: 23}, - PerVolumeGigabytes: quotasets.QuotaUsage{InUse: 24, Limit: 25, Reserved: 26}, - Backups: quotasets.QuotaUsage{InUse: 27, Limit: 28, Reserved: 29}, - BackupGigabytes: quotasets.QuotaUsage{InUse: 30, Limit: 31, Reserved: 32}, -} - -var fullUpdateExpectedJSONBody = ` -{ - "quota_set": { - "volumes": 8, - "snapshots": 9, - "gigabytes": 10, - "per_volume_gigabytes": 11, - "backups": 12, - "backup_gigabytes": 13 - } -}` - -var fullUpdateOpts = quotasets.UpdateOpts{ - Volumes: golangsdk.IntToPointer(8), - Snapshots: golangsdk.IntToPointer(9), - Gigabytes: golangsdk.IntToPointer(10), - PerVolumeGigabytes: golangsdk.IntToPointer(11), - Backups: golangsdk.IntToPointer(12), - BackupGigabytes: golangsdk.IntToPointer(13), -} - -var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ - Volumes: 8, - Snapshots: 9, - Gigabytes: 10, - PerVolumeGigabytes: 11, - Backups: 12, - BackupGigabytes: 13, -} - -var partialUpdateExpectedJSONBody = ` -{ - "quota_set": { - "volumes": 200, - "snapshots": 0, - "gigabytes": 0, - "per_volume_gigabytes": 0, - "backups": 0, - "backup_gigabytes": 0 - } -}` - -var partialUpdateOpts = quotasets.UpdateOpts{ - Volumes: golangsdk.IntToPointer(200), - Snapshots: golangsdk.IntToPointer(0), - Gigabytes: golangsdk.IntToPointer(0), - PerVolumeGigabytes: golangsdk.IntToPointer(0), - Backups: golangsdk.IntToPointer(0), - BackupGigabytes: golangsdk.IntToPointer(0), -} - -var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{Volumes: 200} - -// HandleSuccessfulRequest configures the test server to respond to an HTTP request. -func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput string, uriQueryParams map[string]string) { - - th.Mux.HandleFunc(uriPath, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, httpMethod) - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - w.Header().Add("Content-Type", "application/json") - - if uriQueryParams != nil { - th.TestFormValues(t, r, uriQueryParams) - } - - _, _ = fmt.Fprint(w, jsonOutput) - }) -} - -// HandleDeleteSuccessfully tests quotaset deletion. -func HandleDeleteSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.WriteHeader(http.StatusOK) - }) -} diff --git a/openstack/evs/extensions/quotasets/testing/requests_test.go b/openstack/evs/extensions/quotasets/testing/requests_test.go deleted file mode 100644 index ccb67f398..000000000 --- a/openstack/evs/extensions/quotasets/testing/requests_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/quotasets" - - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" - "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" -) - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - uriQueryParms := map[string]string{} - HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &getExpectedQuotaSet, actual) -} - -func TestGetUsage(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - uriQueryParms := map[string]string{"usage": "true"} - HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getUsageExpectedJSONBody, uriQueryParms) - actual, err := quotasets.GetUsage(client.ServiceClient(), FirstTenantID) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &getUsageExpectedQuotaSet, actual) -} - -func TestFullUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - uriQueryParms := map[string]string{} - HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, fullUpdateExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, fullUpdateOpts) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &fullUpdateExpectedQuotaSet, actual) -} - -func TestPartialUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - uriQueryParms := map[string]string{} - HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, partialUpdateExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, partialUpdateOpts) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &partiualUpdateExpectedQuotaSet, actual) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleDeleteSuccessfully(t) - - err := quotasets.Delete(client.ServiceClient(), FirstTenantID) - th.AssertNoErr(t, err) -} diff --git a/openstack/evs/extensions/quotasets/update.go b/openstack/evs/extensions/quotasets/update.go index aef568063..a774bf43a 100644 --- a/openstack/evs/extensions/quotasets/update.go +++ b/openstack/evs/extensions/quotasets/update.go @@ -2,11 +2,12 @@ package quotasets import ( "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" ) func Update(client *golangsdk.ServiceClient, projectID string, opts UpdateOpts) (*QuotaSet, error) { - b, err := golangsdk.BuildRequestBody(opts, "quota_set") + b, err := build.RequestBody(opts, "quota_set") if err != nil { return nil, err } diff --git a/openstack/evs/extensions/quotasets/urls.go b/openstack/evs/extensions/quotasets/urls.go deleted file mode 100644 index 31c87e175..000000000 --- a/openstack/evs/extensions/quotasets/urls.go +++ /dev/null @@ -1,21 +0,0 @@ -package quotasets - -import "github.com/opentelekomcloud/gophertelekomcloud" - -const resourcePath = "os-quota-sets" - -func getURL(c *golangsdk.ServiceClient, projectID string) string { - return c.ServiceURL(resourcePath, projectID) -} - -func getDefaultsURL(c *golangsdk.ServiceClient, projectID string) string { - return c.ServiceURL(resourcePath, projectID, "defaults") -} - -func updateURL(c *golangsdk.ServiceClient, projectID string) string { - return getURL(c, projectID) -} - -func deleteURL(c *golangsdk.ServiceClient, projectID string) string { - return getURL(c, projectID) -} From f04f739a9279fe5ef824dbfd4a1f2e62297b2481 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 15:42:22 +0100 Subject: [PATCH 20/51] List --- openstack/evs/v3/volumes/List.go | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/openstack/evs/v3/volumes/List.go b/openstack/evs/v3/volumes/List.go index dfcc636af..725dac15f 100644 --- a/openstack/evs/v3/volumes/List.go +++ b/openstack/evs/v3/volumes/List.go @@ -8,25 +8,34 @@ import ( // ListOpts holds options for listing Volumes. It is passed to the volumes.List function. type ListOpts struct { - // AllTenants will retrieve volumes of all tenants/projects. - AllTenants bool `q:"all_tenants"` + // Specifies the AZ. + AvailabilityZone bool `q:"availability_zone"` // Metadata will filter results based on specified metadata. Metadata map[string]string `q:"metadata"` - // Name will filter by the specified volume name. + // Specifies the disk name. The value can contain a maximum of 255 bytes. Name string `q:"name"` // Status will filter by the specified status. Status string `q:"status"` // TenantID will filter by a specific tenant/project ID. // Setting AllTenants is required for this. TenantID string `q:"project_id"` - // Comma-separated list of sort keys and optional sort directions in the - // form of [:]. - Sort string `q:"sort"` - // Requests a page size of items. + // Specifies the keyword based on which the returned results are sorted. + // The value can be id, status, size, or created_at, and the default value is created_at. + Sort string `q:"sort_key"` + // Specifies the result sorting order. The default value is desc. + // desc: specifies the descending order. + // asc: specifies the ascending order. + SortDir string `q:"sort_dir"` + // Specifies the maximum number of query results that can be returned. + // The value ranges from 1 to 1000, and the default value is 1000. The returned value cannot exceed this limit. + // If the tenant has more than 50 disks in total, you are advised to use this parameter and set its value to 50 to improve the query efficiency. + // Examples are provided as follows: + // GET /v3/xxx/volumes?limit=50: Queries the 1–50 disks. GET /v3/xxx/volumes?offset=50&limit=50: Queries the 51–100 disks. Limit int `q:"limit"` - // Used in conjunction with limit to return a slice of items. + // Specifies the offset. + // All disks after this offset will be queried. The value must be an integer greater than 0 but less than the number of disks. Offset int `q:"offset"` - // The ID of the last-seen item. + // Specifies the ID of the last record on the previous page. The returned value is the value of the item after this one. Marker string `q:"marker"` } @@ -37,7 +46,8 @@ func List(client *golangsdk.ServiceClient, opts ListOpts) pagination.Pager { return pagination.Pager{Err: err} } - return pagination.NewPager(client, client.ServiceURL("volumes", "detail")+q.String(), + // GET /v3/{project_id}/volumes + return pagination.NewPager(client, client.ServiceURL("volumes")+q.String(), func(r pagination.PageResult) pagination.Page { return VolumePage{pagination.LinkedPageBase{PageResult: r}} }) From eaafa7b0263b44af333e70ed2fa07168818117ca Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 16:21:58 +0100 Subject: [PATCH 21/51] QuotaSet --- openstack/evs/extensions/quotasets/get.go | 81 ++++++++++++++++------- 1 file changed, 58 insertions(+), 23 deletions(-) diff --git a/openstack/evs/extensions/quotasets/get.go b/openstack/evs/extensions/quotasets/get.go index 8bb192174..051b3dff0 100644 --- a/openstack/evs/extensions/quotasets/get.go +++ b/openstack/evs/extensions/quotasets/get.go @@ -6,34 +6,69 @@ import ( ) func Get(client *golangsdk.ServiceClient, projectID string) (*QuotaSet, error) { - raw, err := client.Get(client.ServiceURL("os-quota-sets", projectID), nil, nil) + // GET /v3/{project_id}/os-quota-sets/{target_project_id}?usage=True + raw, err := client.Get(client.ServiceURL("os-quota-sets", projectID)+"?usage=True", nil, nil) if err != nil { return nil, err } - var res struct { - QuotaSet QuotaSet `json:"quota_set"` - } - err = extract.Into(raw.Body, &res) - return &res.QuotaSet, err + var res QuotaSet + err = extract.IntoStructPtr(raw.Body, &res, "quota_set") + return &res, err +} + +type Detail struct { + Reserved int `json:"reserved"` + Limit int `json:"limit"` + InUse int `json:"in_use"` } type QuotaSet struct { - // ID is project associated with this QuotaSet. - ID string `json:"id"` - // Volumes is the number of volumes that are allowed for each project. - Volumes int `json:"volumes"` - // Snapshots is the number of snapshots that are allowed for each project. - Snapshots int `json:"snapshots"` - // Gigabytes is the size (GB) of volumes and snapshots that are allowed for - // each project. - Gigabytes int `json:"gigabytes"` - // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are - // allowed for each project and the specifed volume type. - PerVolumeGigabytes int `json:"per_volume_gigabytes"` - // Backups is the number of backups that are allowed for each project. - Backups int `json:"backups"` - // BackupGigabytes is the size (GB) of backups that are allowed for each - // project. - BackupGigabytes int `json:"backup_gigabytes"` + // Specifies the size (GB) reserved for common I/O disks. Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + GigabytesSAS Detail `json:"gigabytes_SAS"` + // Specifies the number of reserved common I/O disks. Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + VolumesSATA Detail `json:"volumes_SATA"` + // Specifies the total size (GB) of disks and snapshots allowed. Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + Gigabytes Detail `json:"gigabytes"` + // Specifies the backup size (GB). Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + BackupGigabytes Detail `json:"backup_gigabytes"` + // Specifies the number of snapshots reserved for high I/O disks. Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + SnapshotsSAS Detail `json:"snapshots_SAS"` + // Specifies the number of reserved ultra-high I/O disks. Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + VolumesSSD Detail `json:"volumes_SSD"` + // Specifies the number of snapshots. Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + Snapshots Detail `json:"snapshots"` + // Specifies the tenant ID. The tenant ID is actually the project ID. + Id string `json:"id"` + // Specifies the number of reserved high I/O disks. Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + VolumesSAS Detail `json:"volumes_SAS"` + // Specifies the number of snapshots reserved for ultra-high I/O disks. Sub-parameters include reserved + // (reserved quota), limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + SnapshotsSSD Detail `json:"snapshots_SSD"` + // Specifies the number of disks. Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + Volumes Detail `json:"volumes"` + // Specifies the backup size (GB). Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + GigabytesSATA Detail `json:"gigabytes_SATA"` + // Specifies the number of backups. Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + Backups Detail `json:"backups"` + // Specifies the size (GB) reserved for ultra-high I/O disks. Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + GigabytesSSD Detail `json:"gigabytes_SSD"` + // Specifies the number of snapshots reserved for common I/O disks. Sub-parameters include reserved + // (reserved quota), limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + SnapshotsSATA Detail `json:"snapshots_SATA"` + // Specifies the capacity quota of each EVS disk. Sub-parameters include reserved (reserved quota), + // limit (maximum quota), and in_use (used quota), and are made up of key-value pairs. + PerVolumeGigabytes Detail `json:"per_volume_gigabytes"` } From a397d3188dd60a0eba1858856448eb315cb37b35 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 16:32:24 +0100 Subject: [PATCH 22/51] metadata --- openstack/evs/v2/snapshots/doc.go | 5 ---- openstack/evs/v2/snapshots/requests.go | 10 +++---- openstack/evs/v2/snapshots/urls.go | 27 ------------------- openstack/evs/v2/tags/requests.go | 4 +-- openstack/evs/v2/tags/urls.go | 13 --------- openstack/evs/v2/volumes/doc.go | 5 ---- openstack/evs/v2/volumes/requests.go | 10 +++---- openstack/evs/v2/volumes/testing/doc.go | 2 -- .../evs/v2/volumes/testing/requests_test.go | 2 +- openstack/evs/v2/volumes/urls.go | 23 ---------------- openstack/evs/v3/volumes/metadata/Create.go | 1 + openstack/evs/v3/volumes/metadata/Delete.go | 1 + openstack/evs/v3/volumes/metadata/Get.go | 1 + openstack/evs/v3/volumes/metadata/GetOne.go | 1 + openstack/evs/v3/volumes/metadata/Update.go | 1 + .../evs/v3/volumes/metadata/UpdateOne.go | 1 + 16 files changed, 19 insertions(+), 88 deletions(-) delete mode 100644 openstack/evs/v2/snapshots/doc.go delete mode 100644 openstack/evs/v2/snapshots/urls.go delete mode 100644 openstack/evs/v2/tags/urls.go delete mode 100644 openstack/evs/v2/volumes/doc.go delete mode 100644 openstack/evs/v2/volumes/testing/doc.go delete mode 100644 openstack/evs/v2/volumes/urls.go create mode 100644 openstack/evs/v3/volumes/metadata/Create.go create mode 100644 openstack/evs/v3/volumes/metadata/Delete.go create mode 100644 openstack/evs/v3/volumes/metadata/Get.go create mode 100644 openstack/evs/v3/volumes/metadata/GetOne.go create mode 100644 openstack/evs/v3/volumes/metadata/Update.go create mode 100644 openstack/evs/v3/volumes/metadata/UpdateOne.go diff --git a/openstack/evs/v2/snapshots/doc.go b/openstack/evs/v2/snapshots/doc.go deleted file mode 100644 index 198f83077..000000000 --- a/openstack/evs/v2/snapshots/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package snapshots provides information and interaction with snapshots in the -// OpenStack Block Storage service. A snapshot is a point in time copy of the -// data contained in an external storage volume, and can be controlled -// programmatically. -package snapshots diff --git a/openstack/evs/v2/snapshots/requests.go b/openstack/evs/v2/snapshots/requests.go index 6ada877af..7f2ce2d7d 100644 --- a/openstack/evs/v2/snapshots/requests.go +++ b/openstack/evs/v2/snapshots/requests.go @@ -26,7 +26,7 @@ func Create(client *golangsdk.ServiceClient, opts CreateOpts) (r CreateResult) { return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ + _, r.Err = client.Post(client.ServiceURL("snapshots"), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) return @@ -34,14 +34,14 @@ func Create(client *golangsdk.ServiceClient, opts CreateOpts) (r CreateResult) { // Delete will delete the existing Snapshot with the provided ID. func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + _, r.Err = client.Delete(client.ServiceURL("snapshots", id), nil) return } // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + _, r.Err = client.Get(client.ServiceURL("snapshots", id), &r.Body, nil) return } @@ -83,7 +83,7 @@ func (opts ListOpts) ToSnapshotListQuery() (string, error) { // List returns Snapshots optionally limited by the conditions provided in // ListOpts. func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) + url := client.ServiceURL("snapshots") if opts != nil { query, err := opts.ToSnapshotListQuery() if err != nil { @@ -124,7 +124,7 @@ func UpdateMetadata(client *golangsdk.ServiceClient, id string, opts UpdateMetad r.Err = err return } - _, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ + _, r.Err = client.Put(client.ServiceURL("snapshots", id, "metadata"), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) return diff --git a/openstack/evs/v2/snapshots/urls.go b/openstack/evs/v2/snapshots/urls.go deleted file mode 100644 index 337009274..000000000 --- a/openstack/evs/v2/snapshots/urls.go +++ /dev/null @@ -1,27 +0,0 @@ -package snapshots - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("snapshots") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("snapshots", id) -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) -} - -func listURL(c *golangsdk.ServiceClient) string { - return createURL(c) -} - -func metadataURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("snapshots", id, "metadata") -} - -func updateMetadataURL(c *golangsdk.ServiceClient, id string) string { - return metadataURL(c, id) -} diff --git a/openstack/evs/v2/tags/requests.go b/openstack/evs/v2/tags/requests.go index 62d06e709..65c713905 100644 --- a/openstack/evs/v2/tags/requests.go +++ b/openstack/evs/v2/tags/requests.go @@ -35,13 +35,13 @@ func Create(client *golangsdk.ServiceClient, resource_type, resource_id string, r.Err = err return r } - _, r.Err = client.Put(createURL(client, resource_type, resource_id), b, &r.Body, &golangsdk.RequestOpts{OkCodes: []int{200}}) + _, r.Err = client.Put(client.ServiceURL("os-vendor-tags", resource_type, resource_id), b, &r.Body, &golangsdk.RequestOpts{OkCodes: []int{200}}) return } // Get implements tags get request func Get(client *golangsdk.ServiceClient, resource_type, resource_id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, resource_type, resource_id), &r.Body, nil) + _, r.Err = client.Get(client.ServiceURL("os-vendor-tags", resource_type, resource_id), &r.Body, nil) return } diff --git a/openstack/evs/v2/tags/urls.go b/openstack/evs/v2/tags/urls.go deleted file mode 100644 index 47ee57725..000000000 --- a/openstack/evs/v2/tags/urls.go +++ /dev/null @@ -1,13 +0,0 @@ -package tags - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -func createURL(c *golangsdk.ServiceClient, resource_type, resource_id string) string { - return c.ServiceURL("os-vendor-tags", resource_type, resource_id) -} - -func getURL(c *golangsdk.ServiceClient, resource_type, resource_id string) string { - return c.ServiceURL("os-vendor-tags", resource_type, resource_id) -} diff --git a/openstack/evs/v2/volumes/doc.go b/openstack/evs/v2/volumes/doc.go deleted file mode 100644 index 307b8b12d..000000000 --- a/openstack/evs/v2/volumes/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package volumes provides information and interaction with volumes in the -// OpenStack Block Storage service. A volume is a detachable block storage -// device, akin to a USB hard drive. It can only be attached to one instance at -// a time. -package volumes diff --git a/openstack/evs/v2/volumes/requests.go b/openstack/evs/v2/volumes/requests.go index b88ff2e47..19c46d3c7 100644 --- a/openstack/evs/v2/volumes/requests.go +++ b/openstack/evs/v2/volumes/requests.go @@ -57,7 +57,7 @@ func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateRe r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ + _, r.Err = client.Post(client.ServiceURL("volumes"), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) return @@ -84,7 +84,7 @@ func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { // Delete will delete the existing Volume with the provided ID func Delete(client *golangsdk.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { - url := deleteURL(client, id) + url := client.ServiceURL("volumes", id) if opts != nil { q, err := opts.ToVolumeDeleteQuery() if err != nil { @@ -100,7 +100,7 @@ func Delete(client *golangsdk.ServiceClient, id string, opts DeleteOptsBuilder) // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + _, r.Err = client.Get(client.ServiceURL("volumes", id), &r.Body, nil) return } @@ -154,7 +154,7 @@ func (opts ListOpts) ToVolumeListQuery() (string, error) { // List returns Volumes optionally limited by the conditions provided in ListOpts. func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) + url := client.ServiceURL("volumes", "detail") if opts != nil { query, err := opts.ToVolumeListQuery() if err != nil { @@ -197,7 +197,7 @@ func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ + _, r.Err = client.Put(client.ServiceURL("volumes", id), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) return diff --git a/openstack/evs/v2/volumes/testing/doc.go b/openstack/evs/v2/volumes/testing/doc.go deleted file mode 100644 index aa8351ab1..000000000 --- a/openstack/evs/v2/volumes/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// volumes_v2 -package testing diff --git a/openstack/evs/v2/volumes/testing/requests_test.go b/openstack/evs/v2/volumes/testing/requests_test.go index fba0caea1..4c18800e6 100644 --- a/openstack/evs/v2/volumes/testing/requests_test.go +++ b/openstack/evs/v2/volumes/testing/requests_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/blockstorage/v2/volumes" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v2/volumes" "github.com/opentelekomcloud/gophertelekomcloud/pagination" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" "github.com/opentelekomcloud/gophertelekomcloud/testhelper/client" diff --git a/openstack/evs/v2/volumes/urls.go b/openstack/evs/v2/volumes/urls.go deleted file mode 100644 index f145a5be2..000000000 --- a/openstack/evs/v2/volumes/urls.go +++ /dev/null @@ -1,23 +0,0 @@ -package volumes - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("volumes") -} - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("volumes", "detail") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("volumes", id) -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) -} - -func updateURL(c *golangsdk.ServiceClient, id string) string { - return deleteURL(c, id) -} diff --git a/openstack/evs/v3/volumes/metadata/Create.go b/openstack/evs/v3/volumes/metadata/Create.go new file mode 100644 index 000000000..82c48461b --- /dev/null +++ b/openstack/evs/v3/volumes/metadata/Create.go @@ -0,0 +1 @@ +package metadata diff --git a/openstack/evs/v3/volumes/metadata/Delete.go b/openstack/evs/v3/volumes/metadata/Delete.go new file mode 100644 index 000000000..82c48461b --- /dev/null +++ b/openstack/evs/v3/volumes/metadata/Delete.go @@ -0,0 +1 @@ +package metadata diff --git a/openstack/evs/v3/volumes/metadata/Get.go b/openstack/evs/v3/volumes/metadata/Get.go new file mode 100644 index 000000000..82c48461b --- /dev/null +++ b/openstack/evs/v3/volumes/metadata/Get.go @@ -0,0 +1 @@ +package metadata diff --git a/openstack/evs/v3/volumes/metadata/GetOne.go b/openstack/evs/v3/volumes/metadata/GetOne.go new file mode 100644 index 000000000..82c48461b --- /dev/null +++ b/openstack/evs/v3/volumes/metadata/GetOne.go @@ -0,0 +1 @@ +package metadata diff --git a/openstack/evs/v3/volumes/metadata/Update.go b/openstack/evs/v3/volumes/metadata/Update.go new file mode 100644 index 000000000..82c48461b --- /dev/null +++ b/openstack/evs/v3/volumes/metadata/Update.go @@ -0,0 +1 @@ +package metadata diff --git a/openstack/evs/v3/volumes/metadata/UpdateOne.go b/openstack/evs/v3/volumes/metadata/UpdateOne.go new file mode 100644 index 000000000..82c48461b --- /dev/null +++ b/openstack/evs/v3/volumes/metadata/UpdateOne.go @@ -0,0 +1 @@ +package metadata From 83c83947c05fa32d19ef3a1459d89a0ff07deb27 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 16:51:28 +0100 Subject: [PATCH 23/51] Create --- openstack/evs/v3/volumes/metadata/Create.go | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/openstack/evs/v3/volumes/metadata/Create.go b/openstack/evs/v3/volumes/metadata/Create.go index 82c48461b..7fca21ca9 100644 --- a/openstack/evs/v3/volumes/metadata/Create.go +++ b/openstack/evs/v3/volumes/metadata/Create.go @@ -1 +1,26 @@ package metadata + +import ( + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +func Create(client *golangsdk.ServiceClient, volumeId string, opts map[string]string) (map[string]string, error) { + b, err := build.RequestBody(opts, "metadata") + if err != nil { + return nil, err + } + + // POST /v3/{project_id}/volumes/{volume_id}/metadata + raw, err := client.Post(client.ServiceURL("volumes", volumeId, "metadata"), b, nil, nil) + if err != nil { + return nil, err + } + + var res struct { + Metadata map[string]string `json:"metadata"` + } + err = extract.Into(raw.Body, &res) + return res.Metadata, err +} From 862111683ecac38bd40eaf328811146fbe7fb08f Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 16:53:39 +0100 Subject: [PATCH 24/51] Get --- openstack/evs/v3/volumes/metadata/Create.go | 6 ++++++ openstack/evs/v3/volumes/metadata/Get.go | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/openstack/evs/v3/volumes/metadata/Create.go b/openstack/evs/v3/volumes/metadata/Create.go index 7fca21ca9..6168e6efd 100644 --- a/openstack/evs/v3/volumes/metadata/Create.go +++ b/openstack/evs/v3/volumes/metadata/Create.go @@ -1,6 +1,8 @@ package metadata import ( + "net/http" + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/internal/build" "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" @@ -14,6 +16,10 @@ func Create(client *golangsdk.ServiceClient, volumeId string, opts map[string]st // POST /v3/{project_id}/volumes/{volume_id}/metadata raw, err := client.Post(client.ServiceURL("volumes", volumeId, "metadata"), b, nil, nil) + return extra(err, raw) +} + +func extra(err error, raw *http.Response) (map[string]string, error) { if err != nil { return nil, err } diff --git a/openstack/evs/v3/volumes/metadata/Get.go b/openstack/evs/v3/volumes/metadata/Get.go index 82c48461b..da32085e2 100644 --- a/openstack/evs/v3/volumes/metadata/Get.go +++ b/openstack/evs/v3/volumes/metadata/Get.go @@ -1 +1,9 @@ package metadata + +import golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + +func Get(client *golangsdk.ServiceClient, volumeId string) (map[string]string, error) { + // GET /v3/{project_id}/volumes/{volume_id}/metadata + raw, err := client.Get(client.ServiceURL("volumes", volumeId, "metadata"), nil, nil) + return extra(err, raw) +} From 57fa24446b14b5451ca4c871ffbaa45391ee7a39 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 17:14:08 +0100 Subject: [PATCH 25/51] Update --- openstack/evs/v3/volumes/metadata/Create.go | 4 +++- openstack/evs/v3/volumes/metadata/Update.go | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/openstack/evs/v3/volumes/metadata/Create.go b/openstack/evs/v3/volumes/metadata/Create.go index 6168e6efd..dbdf7388b 100644 --- a/openstack/evs/v3/volumes/metadata/Create.go +++ b/openstack/evs/v3/volumes/metadata/Create.go @@ -15,7 +15,9 @@ func Create(client *golangsdk.ServiceClient, volumeId string, opts map[string]st } // POST /v3/{project_id}/volumes/{volume_id}/metadata - raw, err := client.Post(client.ServiceURL("volumes", volumeId, "metadata"), b, nil, nil) + raw, err := client.Post(client.ServiceURL("volumes", volumeId, "metadata"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) return extra(err, raw) } diff --git a/openstack/evs/v3/volumes/metadata/Update.go b/openstack/evs/v3/volumes/metadata/Update.go index 82c48461b..1d4799860 100644 --- a/openstack/evs/v3/volumes/metadata/Update.go +++ b/openstack/evs/v3/volumes/metadata/Update.go @@ -1 +1,19 @@ package metadata + +import ( + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) + +func Update(client *golangsdk.ServiceClient, volumeId string, opts map[string]string) (map[string]string, error) { + b, err := build.RequestBody(opts, "metadata") + if err != nil { + return nil, err + } + + // PUT /v3/{project_id}/volumes/{volume_id}/metadata + raw, err := client.Put(client.ServiceURL("volumes", volumeId, "metadata"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return extra(err, raw) +} From da6e8defdde3cea79b019996e653ab4bca99955a Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 17:20:21 +0100 Subject: [PATCH 26/51] One --- openstack/evs/v3/volumes/metadata/Create.go | 4 +-- openstack/evs/v3/volumes/metadata/Delete.go | 10 ++++++++ openstack/evs/v3/volumes/metadata/Get.go | 2 +- openstack/evs/v3/volumes/metadata/GetOne.go | 25 +++++++++++++++++++ openstack/evs/v3/volumes/metadata/Update.go | 2 +- .../evs/v3/volumes/metadata/UpdateOne.go | 18 +++++++++++++ 6 files changed, 57 insertions(+), 4 deletions(-) diff --git a/openstack/evs/v3/volumes/metadata/Create.go b/openstack/evs/v3/volumes/metadata/Create.go index dbdf7388b..cf5d97d9e 100644 --- a/openstack/evs/v3/volumes/metadata/Create.go +++ b/openstack/evs/v3/volumes/metadata/Create.go @@ -18,10 +18,10 @@ func Create(client *golangsdk.ServiceClient, volumeId string, opts map[string]st raw, err := client.Post(client.ServiceURL("volumes", volumeId, "metadata"), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - return extra(err, raw) + return extraMetadata(err, raw) } -func extra(err error, raw *http.Response) (map[string]string, error) { +func extraMetadata(err error, raw *http.Response) (map[string]string, error) { if err != nil { return nil, err } diff --git a/openstack/evs/v3/volumes/metadata/Delete.go b/openstack/evs/v3/volumes/metadata/Delete.go index 82c48461b..31c7479de 100644 --- a/openstack/evs/v3/volumes/metadata/Delete.go +++ b/openstack/evs/v3/volumes/metadata/Delete.go @@ -1 +1,11 @@ package metadata + +import golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + +func Delete(client *golangsdk.ServiceClient, volumeId string, key string) (err error) { + // DELETE /v3/{project_id}/volumes/{volume_id}/metadata/{key} + _, err = client.Delete(client.ServiceURL("volumes", volumeId, "metadata", key), &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/evs/v3/volumes/metadata/Get.go b/openstack/evs/v3/volumes/metadata/Get.go index da32085e2..fd86dba01 100644 --- a/openstack/evs/v3/volumes/metadata/Get.go +++ b/openstack/evs/v3/volumes/metadata/Get.go @@ -5,5 +5,5 @@ import golangsdk "github.com/opentelekomcloud/gophertelekomcloud" func Get(client *golangsdk.ServiceClient, volumeId string) (map[string]string, error) { // GET /v3/{project_id}/volumes/{volume_id}/metadata raw, err := client.Get(client.ServiceURL("volumes", volumeId, "metadata"), nil, nil) - return extra(err, raw) + return extraMetadata(err, raw) } diff --git a/openstack/evs/v3/volumes/metadata/GetOne.go b/openstack/evs/v3/volumes/metadata/GetOne.go index 82c48461b..1eb3a5462 100644 --- a/openstack/evs/v3/volumes/metadata/GetOne.go +++ b/openstack/evs/v3/volumes/metadata/GetOne.go @@ -1 +1,26 @@ package metadata + +import ( + "net/http" + + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +func GetOne(client *golangsdk.ServiceClient, volumeId string, key string) (map[string]string, error) { + // GET /v3/{project_id}/volumes/{volume_id}/metadata/{key} + raw, err := client.Get(client.ServiceURL("volumes", volumeId, "metadata", key), nil, nil) + return extraMeta(err, raw) +} + +func extraMeta(err error, raw *http.Response) (map[string]string, error) { + if err != nil { + return nil, err + } + + var res struct { + Metadata map[string]string `json:"meta"` + } + err = extract.Into(raw.Body, &res) + return res.Metadata, err +} diff --git a/openstack/evs/v3/volumes/metadata/Update.go b/openstack/evs/v3/volumes/metadata/Update.go index 1d4799860..cfefb04e0 100644 --- a/openstack/evs/v3/volumes/metadata/Update.go +++ b/openstack/evs/v3/volumes/metadata/Update.go @@ -15,5 +15,5 @@ func Update(client *golangsdk.ServiceClient, volumeId string, opts map[string]st raw, err := client.Put(client.ServiceURL("volumes", volumeId, "metadata"), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) - return extra(err, raw) + return extraMetadata(err, raw) } diff --git a/openstack/evs/v3/volumes/metadata/UpdateOne.go b/openstack/evs/v3/volumes/metadata/UpdateOne.go index 82c48461b..94559bc48 100644 --- a/openstack/evs/v3/volumes/metadata/UpdateOne.go +++ b/openstack/evs/v3/volumes/metadata/UpdateOne.go @@ -1 +1,19 @@ package metadata + +import ( + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) + +func UpdateOne(client *golangsdk.ServiceClient, volumeId string, key string, opts map[string]string) (map[string]string, error) { + b, err := build.RequestBody(opts, "meta") + if err != nil { + return nil, err + } + + // PUT /v3/{project_id}/volumes/{volume_id}/metadata/{key} + raw, err := client.Put(client.ServiceURL("volumes", volumeId, "metadata", key), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return extraMeta(err, raw) +} From 90f815cd9834bfc1fb7b0a1c09aece6b233461b6 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 17:26:03 +0100 Subject: [PATCH 27/51] ListAvailabilityZone --- .../evs/extensions/ListAvailabilityZone.go | 33 +++++++++++++++++++ .../evs/extensions/availabilityzones/doc.go | 21 ------------ .../extensions/availabilityzones/requests.go | 13 -------- .../extensions/availabilityzones/results.go | 33 ------------------- .../evs/extensions/availabilityzones/urls.go | 7 ---- 5 files changed, 33 insertions(+), 74 deletions(-) create mode 100644 openstack/evs/extensions/ListAvailabilityZone.go delete mode 100644 openstack/evs/extensions/availabilityzones/doc.go delete mode 100644 openstack/evs/extensions/availabilityzones/requests.go delete mode 100644 openstack/evs/extensions/availabilityzones/results.go delete mode 100644 openstack/evs/extensions/availabilityzones/urls.go diff --git a/openstack/evs/extensions/ListAvailabilityZone.go b/openstack/evs/extensions/ListAvailabilityZone.go new file mode 100644 index 000000000..dd08f8695 --- /dev/null +++ b/openstack/evs/extensions/ListAvailabilityZone.go @@ -0,0 +1,33 @@ +package extensions + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +// ListAvailabilityZone will return the existing availability zones. +func ListAvailabilityZone(client *golangsdk.ServiceClient) ([]AvailabilityZone, error) { + // GET /v3/{project_id}/os-availability-zone + raw, err := client.Get(client.ServiceURL("os-availability-zone"), nil, nil) + if err != nil { + return nil, err + } + + var res []AvailabilityZone + err = extract.IntoSlicePtr(raw.Body, &res, "availabilityZoneInfo") + return res, err +} + +// ZoneState represents the current state of the availability zone. +type ZoneState struct { + // Returns true if the availability zone is available + Available bool `json:"available"` +} + +// AvailabilityZone contains all the information associated with an OpenStack +// AvailabilityZone. +type AvailabilityZone struct { + // The availability zone name + ZoneName string `json:"zoneName"` + ZoneState ZoneState `json:"zoneState"` +} diff --git a/openstack/evs/extensions/availabilityzones/doc.go b/openstack/evs/extensions/availabilityzones/doc.go deleted file mode 100644 index 29faa8dcb..000000000 --- a/openstack/evs/extensions/availabilityzones/doc.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Package availabilityzones provides the ability to get lists of -available volume availability zones. - -Example of Get Availability Zone Information - - allPages, err := availabilityzones.List(volumeClient).AllPages() - if err != nil { - panic(err) - } - - availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) - if err != nil { - panic(err) - } - - for _, zoneInfo := range availabilityZoneInfo { - fmt.Printf("%+v\n", zoneInfo) - } -*/ -package availabilityzones diff --git a/openstack/evs/extensions/availabilityzones/requests.go b/openstack/evs/extensions/availabilityzones/requests.go deleted file mode 100644 index d4b270eec..000000000 --- a/openstack/evs/extensions/availabilityzones/requests.go +++ /dev/null @@ -1,13 +0,0 @@ -package availabilityzones - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// List will return the existing availability zones. -func List(client *golangsdk.ServiceClient) pagination.Pager { - return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { - return AvailabilityZonePage{pagination.SinglePageBase(r)} - }) -} diff --git a/openstack/evs/extensions/availabilityzones/results.go b/openstack/evs/extensions/availabilityzones/results.go deleted file mode 100644 index 59599f00c..000000000 --- a/openstack/evs/extensions/availabilityzones/results.go +++ /dev/null @@ -1,33 +0,0 @@ -package availabilityzones - -import ( - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// ZoneState represents the current state of the availability zone. -type ZoneState struct { - // Returns true if the availability zone is available - Available bool `json:"available"` -} - -// AvailabilityZone contains all the information associated with an OpenStack -// AvailabilityZone. -type AvailabilityZone struct { - // The availability zone name - ZoneName string `json:"zoneName"` - ZoneState ZoneState `json:"zoneState"` -} - -type AvailabilityZonePage struct { - pagination.SinglePageBase -} - -// ExtractAvailabilityZones returns a slice of AvailabilityZones contained in a -// single page of results. -func ExtractAvailabilityZones(r pagination.Page) ([]AvailabilityZone, error) { - var s struct { - AvailabilityZoneInfo []AvailabilityZone `json:"availabilityZoneInfo"` - } - err := (r.(AvailabilityZonePage)).ExtractInto(&s) - return s.AvailabilityZoneInfo, err -} diff --git a/openstack/evs/extensions/availabilityzones/urls.go b/openstack/evs/extensions/availabilityzones/urls.go deleted file mode 100644 index d298c15d5..000000000 --- a/openstack/evs/extensions/availabilityzones/urls.go +++ /dev/null @@ -1,7 +0,0 @@ -package availabilityzones - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("os-availability-zone") -} From a8f74851dbeff6a312dfb93c8803337bde3509ba Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 3 Nov 2022 17:33:01 +0100 Subject: [PATCH 28/51] ExtendSize --- .../evs/extensions/volumeactions/attach.go | 7 +- .../evs/extensions/volumeactions/detach.go | 7 +- .../extensions/volumeactions/extend_size.go | 10 +- .../evs/extensions/volumeactions/requests.go | 290 +----------------- .../evs/extensions/volumeactions/results.go | 116 ------- .../volumeactions/terminate_connection.go | 7 +- .../extensions/volumeactions/upload_image.go | 3 +- .../evs/extensions/volumeactions/urls.go | 7 - 8 files changed, 28 insertions(+), 419 deletions(-) delete mode 100644 openstack/evs/extensions/volumeactions/urls.go diff --git a/openstack/evs/extensions/volumeactions/attach.go b/openstack/evs/extensions/volumeactions/attach.go index 0d176c4e3..8b82a4078 100644 --- a/openstack/evs/extensions/volumeactions/attach.go +++ b/openstack/evs/extensions/volumeactions/attach.go @@ -1,6 +1,9 @@ package volumeactions -import "github.com/opentelekomcloud/gophertelekomcloud" +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) type AttachMode string @@ -21,7 +24,7 @@ type AttachOpts struct { } func Attach(client *golangsdk.ServiceClient, id string, opts AttachOpts) (err error) { - b, err := golangsdk.BuildRequestBody(opts, "os-attach") + b, err := build.RequestBody(opts, "os-attach") if err != nil { return } diff --git a/openstack/evs/extensions/volumeactions/detach.go b/openstack/evs/extensions/volumeactions/detach.go index cd54d2780..c243328ea 100644 --- a/openstack/evs/extensions/volumeactions/detach.go +++ b/openstack/evs/extensions/volumeactions/detach.go @@ -1,6 +1,9 @@ package volumeactions -import "github.com/opentelekomcloud/gophertelekomcloud" +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) type DetachOpts struct { // AttachmentID is the ID of the attachment between a volume and instance. @@ -8,7 +11,7 @@ type DetachOpts struct { } func Detach(client *golangsdk.ServiceClient, id string, opts DetachOpts) (err error) { - b, err := golangsdk.BuildRequestBody(opts, "os-detach") + b, err := build.RequestBody(opts, "os-detach") if err != nil { return } diff --git a/openstack/evs/extensions/volumeactions/extend_size.go b/openstack/evs/extensions/volumeactions/extend_size.go index 28a24f75b..29db2468d 100644 --- a/openstack/evs/extensions/volumeactions/extend_size.go +++ b/openstack/evs/extensions/volumeactions/extend_size.go @@ -1,14 +1,18 @@ package volumeactions -import "github.com/opentelekomcloud/gophertelekomcloud" +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) type ExtendSizeOpts struct { - // NewSize is the new size of the volume, in GB. + // Specifies the size of the disk after capacity expansion, in GB. + // The new disk size ranges from the original disk size to the maximum size (32768 for a data disk and 1024 for a system disk). NewSize int `json:"new_size" required:"true"` } func ExtendSize(client *golangsdk.ServiceClient, id string, opts ExtendSizeOpts) (err error) { - b, err := golangsdk.BuildRequestBody(opts, "os-extend") + b, err := build.RequestBody(opts, "os-extend") if err != nil { return } diff --git a/openstack/evs/extensions/volumeactions/requests.go b/openstack/evs/extensions/volumeactions/requests.go index 39918656a..364b3e3ce 100644 --- a/openstack/evs/extensions/volumeactions/requests.go +++ b/openstack/evs/extensions/volumeactions/requests.go @@ -4,288 +4,6 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud" ) -// AttachOptsBuilder allows extensions to add additional parameters to the -// Attach request. -type AttachOptsBuilder interface { - ToVolumeAttachMap() (map[string]interface{}, error) -} - -// AttachMode describes the attachment mode for volumes. -type AttachMode string - -// These constants determine how a volume is attached. -const ( - ReadOnly AttachMode = "ro" - ReadWrite AttachMode = "rw" -) - -// AttachOpts contains options for attaching a Volume. -type AttachOpts struct { - // The mountpoint of this volume. - MountPoint string `json:"mountpoint,omitempty"` - - // The nova instance ID, can't set simultaneously with HostName. - InstanceUUID string `json:"instance_uuid,omitempty"` - - // The hostname of baremetal host, can't set simultaneously with InstanceUUID. - HostName string `json:"host_name,omitempty"` - - // Mount mode of this volume. - Mode AttachMode `json:"mode,omitempty"` -} - -// ToVolumeAttachMap assembles a request body based on the contents of a -// AttachOpts. -func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "os-attach") -} - -// Attach will attach a volume based on the values in AttachOpts. -func Attach(client *golangsdk.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) { - b, err := opts.ToVolumeAttachMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// BeginDetaching will mark the volume as detaching. -func BeginDetaching(client *golangsdk.ServiceClient, id string) (r BeginDetachingResult) { - b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} - resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// DetachOptsBuilder allows extensions to add additional parameters to the -// Detach request. -type DetachOptsBuilder interface { - ToVolumeDetachMap() (map[string]interface{}, error) -} - -// DetachOpts contains options for detaching a Volume. -type DetachOpts struct { - // AttachmentID is the ID of the attachment between a volume and instance. - AttachmentID string `json:"attachment_id,omitempty"` -} - -// ToVolumeDetachMap assembles a request body based on the contents of a -// DetachOpts. -func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "os-detach") -} - -// Detach will detach a volume based on volume ID. -func Detach(client *golangsdk.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { - b, err := opts.ToVolumeDetachMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Reserve will reserve a volume based on volume ID. -func Reserve(client *golangsdk.ServiceClient, id string) (r ReserveResult) { - b := map[string]interface{}{"os-reserve": make(map[string]interface{})} - resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Unreserve will unreserve a volume based on volume ID. -func Unreserve(client *golangsdk.ServiceClient, id string) (r UnreserveResult) { - b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} - resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// InitializeConnectionOptsBuilder allows extensions to add additional parameters to the -// InitializeConnection request. -type InitializeConnectionOptsBuilder interface { - ToVolumeInitializeConnectionMap() (map[string]interface{}, error) -} - -// InitializeConnectionOpts hosts options for InitializeConnection. -// The fields are specific to the storage driver in use and the destination -// attachment. -type InitializeConnectionOpts struct { - IP string `json:"ip,omitempty"` - Host string `json:"host,omitempty"` - Initiator string `json:"initiator,omitempty"` - Wwpns []string `json:"wwpns,omitempty"` - Wwnns string `json:"wwnns,omitempty"` - Multipath *bool `json:"multipath,omitempty"` - Platform string `json:"platform,omitempty"` - OSType string `json:"os_type,omitempty"` -} - -// ToVolumeInitializeConnectionMap assembles a request body based on the contents of a -// InitializeConnectionOpts. -func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]interface{}, error) { - b, err := golangsdk.BuildRequestBody(opts, "connector") - return map[string]interface{}{"os-initialize_connection": b}, err -} - -// InitializeConnection initializes an iSCSI connection by volume ID. -func InitializeConnection(client *golangsdk.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { - b, err := opts.ToVolumeInitializeConnectionMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// TerminateConnectionOptsBuilder allows extensions to add additional parameters to the -// TerminateConnection request. -type TerminateConnectionOptsBuilder interface { - ToVolumeTerminateConnectionMap() (map[string]interface{}, error) -} - -// TerminateConnectionOpts hosts options for TerminateConnection. -type TerminateConnectionOpts struct { - IP string `json:"ip,omitempty"` - Host string `json:"host,omitempty"` - Initiator string `json:"initiator,omitempty"` - Wwpns []string `json:"wwpns,omitempty"` - Wwnns string `json:"wwnns,omitempty"` - Multipath *bool `json:"multipath,omitempty"` - Platform string `json:"platform,omitempty"` - OSType string `json:"os_type,omitempty"` -} - -// ToVolumeTerminateConnectionMap assembles a request body based on the contents of a -// TerminateConnectionOpts. -func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { - b, err := golangsdk.BuildRequestBody(opts, "connector") - return map[string]interface{}{"os-terminate_connection": b}, err -} - -// TerminateConnection terminates an iSCSI connection by volume ID. -func TerminateConnection(client *golangsdk.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { - b, err := opts.ToVolumeTerminateConnectionMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// ExtendSizeOptsBuilder allows extensions to add additional parameters to the -// ExtendSize request. -type ExtendSizeOptsBuilder interface { - ToVolumeExtendSizeMap() (map[string]interface{}, error) -} - -// ExtendSizeOpts contains options for extending the size of an existing Volume. -// This object is passed to the volumes.ExtendSize function. -type ExtendSizeOpts struct { - // NewSize is the new size of the volume, in GB. - NewSize int `json:"new_size" required:"true"` -} - -// ToVolumeExtendSizeMap assembles a request body based on the contents of an -// ExtendSizeOpts. -func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "os-extend") -} - -// ExtendSize will extend the size of the volume based on the provided information. -// This operation does not return a response body. -func ExtendSize(client *golangsdk.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) { - b, err := opts.ToVolumeExtendSizeMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// UploadImageOptsBuilder allows extensions to add additional parameters to the -// UploadImage request. -type UploadImageOptsBuilder interface { - ToVolumeUploadImageMap() (map[string]interface{}, error) -} - -// UploadImageOpts contains options for uploading a Volume to image storage. -type UploadImageOpts struct { - // Container format, may be bare, ofv, ova, etc. - ContainerFormat string `json:"container_format,omitempty"` - - // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. - DiskFormat string `json:"disk_format,omitempty"` - - // The name of image that will be stored in glance. - ImageName string `json:"image_name,omitempty"` - - // Force image creation, usable if volume attached to instance. - Force bool `json:"force,omitempty"` - - // Visibility defines who can see/use the image. - // supported since 3.1 microversion - Visibility string `json:"visibility,omitempty"` - - // whether the image is not deletable. - // supported since 3.1 microversion - Protected bool `json:"protected,omitempty"` -} - -// ToVolumeUploadImageMap assembles a request body based on the contents of a -// UploadImageOpts. -func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "os-volume_upload_image") -} - -// UploadImage will upload an image based on the values in UploadImageOptsBuilder. -func UploadImage(client *golangsdk.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { - b, err := opts.ToVolumeUploadImageMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// ForceDelete will delete the volume regardless of state. -func ForceDelete(client *golangsdk.ServiceClient, id string) (r ForceDeleteResult) { - resp, err := client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - // ImageMetadataOptsBuilder allows extensions to add additional parameters to the // ImageMetadataRequest request. type ImageMetadataOptsBuilder interface { @@ -311,7 +29,7 @@ func SetImageMetadata(client *golangsdk.ServiceClient, id string, opts ImageMeta r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ + resp, err := client.Post(client.ServiceURL("volumes", id, "action"), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -337,7 +55,7 @@ func SetBootable(client *golangsdk.ServiceClient, id string, opts BootableOpts) r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ + resp, err := client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -385,7 +103,7 @@ func ChangeType(client *golangsdk.ServiceClient, id string, opts ChangeTypeOptsB r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ + resp, err := client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -412,7 +130,7 @@ func ReImage(client *golangsdk.ServiceClient, id string, opts ReImageOpts) (r Re r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &golangsdk.RequestOpts{ + resp, err := client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) diff --git a/openstack/evs/extensions/volumeactions/results.go b/openstack/evs/extensions/volumeactions/results.go index fff8d58f7..e02d2e32e 100644 --- a/openstack/evs/extensions/volumeactions/results.go +++ b/openstack/evs/extensions/volumeactions/results.go @@ -1,9 +1,6 @@ package volumeactions import ( - "encoding/json" - "time" - "github.com/opentelekomcloud/gophertelekomcloud" ) @@ -82,119 +79,6 @@ func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) { return s.ConnectionInfo, err } -// ImageVolumeType contains volume type information obtained from UploadImage -// action. -type ImageVolumeType struct { - // The ID of a volume type. - ID string `json:"id"` - - // Human-readable display name for the volume type. - Name string `json:"name"` - - // Human-readable description for the volume type. - Description string `json:"display_description"` - - // Flag for public access. - IsPublic bool `json:"is_public"` - - // Extra specifications for volume type. - ExtraSpecs map[string]interface{} `json:"extra_specs"` - - // ID of quality of service specs. - QosSpecsID string `json:"qos_specs_id"` - - // Flag for deletion status of volume type. - Deleted bool `json:"deleted"` - - // The date when volume type was deleted. - DeletedAt time.Time `json:"-"` - - // The date when volume type was created. - CreatedAt time.Time `json:"-"` - - // The date when this volume was last updated. - UpdatedAt time.Time `json:"-"` -} - -func (r *ImageVolumeType) UnmarshalJSON(b []byte) error { - type tmp ImageVolumeType - var s struct { - tmp - CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` - DeletedAt golangsdk.JSONRFC3339MilliNoZ `json:"deleted_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = ImageVolumeType(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) - r.DeletedAt = time.Time(s.DeletedAt) - - return err -} - -// VolumeImage contains information about volume uploaded to an image service. -type VolumeImage struct { - // The ID of a volume an image is created from. - VolumeID string `json:"id"` - - // Container format, may be bare, ofv, ova, etc. - ContainerFormat string `json:"container_format"` - - // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. - DiskFormat string `json:"disk_format"` - - // Human-readable description for the volume. - Description string `json:"display_description"` - - // The ID of the created image. - ImageID string `json:"image_id"` - - // Human-readable display name for the image. - ImageName string `json:"image_name"` - - // Size of the volume in GB. - Size int `json:"size"` - - // Current status of the volume. - Status string `json:"status"` - - // Visibility defines who can see/use the image. - // supported since 3.1 microversion - Visibility string `json:"visibility"` - - // whether the image is not deletable. - // supported since 3.1 microversion - Protected bool `json:"protected"` - - // The date when this volume was last updated. - UpdatedAt time.Time `json:"-"` - - // Volume type object of used volume. - VolumeType ImageVolumeType `json:"volume_type"` -} - -func (r *VolumeImage) UnmarshalJSON(b []byte) error { - type tmp VolumeImage - var s struct { - tmp - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = VolumeImage(s.tmp) - - r.UpdatedAt = time.Time(s.UpdatedAt) - - return err -} - // Extract will get an object with info about the uploaded image out of the // UploadImageResult object. func (r UploadImageResult) Extract() (VolumeImage, error) { diff --git a/openstack/evs/extensions/volumeactions/terminate_connection.go b/openstack/evs/extensions/volumeactions/terminate_connection.go index 781943259..276af084e 100644 --- a/openstack/evs/extensions/volumeactions/terminate_connection.go +++ b/openstack/evs/extensions/volumeactions/terminate_connection.go @@ -1,6 +1,9 @@ package volumeactions -import "github.com/opentelekomcloud/gophertelekomcloud" +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) type TerminateConnectionOpts struct { IP string `json:"ip,omitempty"` @@ -14,7 +17,7 @@ type TerminateConnectionOpts struct { } func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { - b, err := golangsdk.BuildRequestBody(opts, "connector") + b, err := build.RequestBody(opts, "connector") return map[string]interface{}{"os-terminate_connection": b}, err } diff --git a/openstack/evs/extensions/volumeactions/upload_image.go b/openstack/evs/extensions/volumeactions/upload_image.go index 5b955f544..d9174f26f 100644 --- a/openstack/evs/extensions/volumeactions/upload_image.go +++ b/openstack/evs/extensions/volumeactions/upload_image.go @@ -5,6 +5,7 @@ import ( "time" "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" ) @@ -20,7 +21,7 @@ type UploadImageOpts struct { } func UploadImage(client *golangsdk.ServiceClient, id string, opts UploadImageOpts) (*VolumeImage, error) { - b, err := golangsdk.BuildRequestBody(opts, "os-volume_upload_image") + b, err := build.RequestBody(opts, "os-volume_upload_image") if err != nil { return nil, err } diff --git a/openstack/evs/extensions/volumeactions/urls.go b/openstack/evs/extensions/volumeactions/urls.go deleted file mode 100644 index fe44e072c..000000000 --- a/openstack/evs/extensions/volumeactions/urls.go +++ /dev/null @@ -1,7 +0,0 @@ -package volumeactions - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func actionURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("volumes", id, "action") -} From f8c9ccd707b80a1f139723a23a9f7a14b0c8e9e5 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Fri, 4 Nov 2022 12:19:51 +0100 Subject: [PATCH 29/51] SetBootable --- .../extensions/volumeactions/SetBootable.go | 25 ++++++++++++++++++ .../evs/extensions/volumeactions/requests.go | 26 ------------------- 2 files changed, 25 insertions(+), 26 deletions(-) create mode 100644 openstack/evs/extensions/volumeactions/SetBootable.go diff --git a/openstack/evs/extensions/volumeactions/SetBootable.go b/openstack/evs/extensions/volumeactions/SetBootable.go new file mode 100644 index 000000000..7f20245b3 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/SetBootable.go @@ -0,0 +1,25 @@ +package volumeactions + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) + +// BootableOpts contains options for setting bootable status to a volume. +type BootableOpts struct { + // Enables or disables the bootable attribute. You can boot an instance from a bootable volume. + Bootable bool `json:"bootable"` +} + +// SetBootable will set bootable status on a volume based on the values in BootableOpts +func SetBootable(client *golangsdk.ServiceClient, id string, opts BootableOpts) (err error) { + b, err := build.RequestBody(opts, "os-set_bootable") + if err != nil { + return + } + + _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/evs/extensions/volumeactions/requests.go b/openstack/evs/extensions/volumeactions/requests.go index 364b3e3ce..ff194bad7 100644 --- a/openstack/evs/extensions/volumeactions/requests.go +++ b/openstack/evs/extensions/volumeactions/requests.go @@ -36,32 +36,6 @@ func SetImageMetadata(client *golangsdk.ServiceClient, id string, opts ImageMeta return } -// BootableOpts contains options for setting bootable status to a volume. -type BootableOpts struct { - // Enables or disables the bootable attribute. You can boot an instance from a bootable volume. - Bootable bool `json:"bootable"` -} - -// ToBootableMap assembles a request body based on the contents of a -// BootableOpts. -func (opts BootableOpts) ToBootableMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "os-set_bootable") -} - -// SetBootable will set bootable status on a volume based on the values in BootableOpts -func SetBootable(client *golangsdk.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { - b, err := opts.ToBootableMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - // MigrationPolicy type represents a migration_policy when changing types. type MigrationPolicy string From 32721e0caa1325fa60e5cc07c377b343270b3ff1 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Fri, 4 Nov 2022 12:25:26 +0100 Subject: [PATCH 30/51] SetReadonly --- .../extensions/volumeactions/SetReadonly.go | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 openstack/evs/extensions/volumeactions/SetReadonly.go diff --git a/openstack/evs/extensions/volumeactions/SetReadonly.go b/openstack/evs/extensions/volumeactions/SetReadonly.go new file mode 100644 index 000000000..5eb177398 --- /dev/null +++ b/openstack/evs/extensions/volumeactions/SetReadonly.go @@ -0,0 +1,20 @@ +package volumeactions + +import ( + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) + +type ReadonlyOpts struct { + Readonly bool `json:"readonly"` +} + +func SetReadonly(client *golangsdk.ServiceClient, id string, opts ReadonlyOpts) (err error) { + b, err := build.RequestBody(opts, "os-update_readonly_flag") + if err != nil { + return + } + + _, err = client.Post(client.ServiceURL("volumes", id, "action"), b, nil, nil) + return +} From 473869940f120f972400866d5df69a0d7546e57c Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Fri, 4 Nov 2022 12:41:33 +0100 Subject: [PATCH 31/51] UploadImage --- .../extensions/volumeactions/upload_image.go | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/openstack/evs/extensions/volumeactions/upload_image.go b/openstack/evs/extensions/volumeactions/upload_image.go index d9174f26f..99d1000f7 100644 --- a/openstack/evs/extensions/volumeactions/upload_image.go +++ b/openstack/evs/extensions/volumeactions/upload_image.go @@ -10,14 +10,27 @@ import ( ) type UploadImageOpts struct { - // Container format, may be bare, ofv, ova, etc. + // Specifies the container type of the exported image. + // The value can be ami, ari, aki, ovf, or bare. The default value is bare. ContainerFormat string `json:"container_format,omitempty"` - // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + // Specifies the format of the exported image. + // The value can be vhd, zvhd, zvhd2, raw, or qcow2. The default value is zvhd2. DiskFormat string `json:"disk_format,omitempty"` - // The name of image that will be stored in glance. + // Specifies the name of the exported image. + // The name cannot start or end with space. + // The name contains 1 to 128 characters. + // The name contains the following characters: uppercase letters, lowercase letters, digits, + // and special characters, such as hyphens (-), periods (.), underscores (_), and spaces. ImageName string `json:"image_name,omitempty"` - // Force image creation, usable if volume attached to instance. + // Specifies whether to forcibly export the image. The default value is false. + // If force is set to false and the disk is in the in-use state, the image cannot be forcibly exported. + // If force is set to true and the disk is in the in-use state, the image can be forcibly exported. Force bool `json:"force,omitempty"` + // Specifies the OS type of the exported image. Currently, only windows and linux are supported. The default value is linux. + // There are two underscores (_) in front of os and one underscore (_) after os. + // This parameter setting takes effect only when the __os_type field is not included in volume_image_metadata and the disk status is available. + // If this parameter is not specified, default value linux is used as the OS type of the image. + OSType string `json:"__os_type,omitempty"` } func UploadImage(client *golangsdk.ServiceClient, id string, opts UploadImageOpts) (*VolumeImage, error) { @@ -33,29 +46,29 @@ func UploadImage(client *golangsdk.ServiceClient, id string, opts UploadImageOpt return nil, err } - var res struct { - VolumeImage VolumeImage `json:"os-volume_upload_image"` - } - err = extract.Into(raw.Body, &res) - return &res.VolumeImage, err + var res VolumeImage + err = extract.IntoStructPtr(raw.Body, &res, "os-volume_upload_image") + return &res, err } type VolumeImage struct { // The ID of a volume an image is created from. VolumeID string `json:"id"` - // Container format, may be bare, ofv, ova, etc. + // Specifies the container type of the exported image. + // The value can be ami, ari, aki, ovf, or bare. The default value is bare. ContainerFormat string `json:"container_format"` - // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + // Specifies the format of the exported image. + // The value can be vhd, zvhd, zvhd2, raw, or qcow2. The default value is vhd. DiskFormat string `json:"disk_format"` // Human-readable description for the volume. Description string `json:"display_description"` - // The ID of the created image. + // Specifies the ID of the exported image. ImageID string `json:"image_id"` - // Human-readable display name for the image. + // Specifies the name of the exported image. ImageName string `json:"image_name"` // Size of the volume in GB. Size int `json:"size"` - // Current status of the volume. + // Specifies the disk status after the image is exported. The correct value is uploading. Status string `json:"status"` // The date when this volume was last updated. UpdatedAt time.Time `json:"-"` @@ -89,6 +102,7 @@ type ImageVolumeType struct { // Flag for public access. IsPublic bool `json:"is_public"` // Extra specifications for volume type. + // volumetypes.ExtraSpecs ExtraSpecs map[string]interface{} `json:"extra_specs"` // ID of quality of service specs. QosSpecsID string `json:"qos_specs_id"` From 0ffd28ff7cf6ea997d5cc1e83dfacba71dd9901d Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Fri, 4 Nov 2022 12:44:26 +0100 Subject: [PATCH 32/51] clr --- .../evs/extensions/volumeactions/requests.go | 112 ------------------ 1 file changed, 112 deletions(-) delete mode 100644 openstack/evs/extensions/volumeactions/requests.go diff --git a/openstack/evs/extensions/volumeactions/requests.go b/openstack/evs/extensions/volumeactions/requests.go deleted file mode 100644 index ff194bad7..000000000 --- a/openstack/evs/extensions/volumeactions/requests.go +++ /dev/null @@ -1,112 +0,0 @@ -package volumeactions - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// ImageMetadataOptsBuilder allows extensions to add additional parameters to the -// ImageMetadataRequest request. -type ImageMetadataOptsBuilder interface { - ToImageMetadataMap() (map[string]interface{}, error) -} - -// ImageMetadataOpts contains options for setting image metadata to a volume. -type ImageMetadataOpts struct { - // The image metadata to add to the volume as a set of metadata key and value pairs. - Metadata map[string]string `json:"metadata"` -} - -// ToImageMetadataMap assembles a request body based on the contents of a -// ImageMetadataOpts. -func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "os-set_image_metadata") -} - -// SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder. -func SetImageMetadata(client *golangsdk.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { - b, err := opts.ToImageMetadataMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("volumes", id, "action"), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// MigrationPolicy type represents a migration_policy when changing types. -type MigrationPolicy string - -// Supported attributes for MigrationPolicy attribute for changeType operations. -const ( - MigrationPolicyNever MigrationPolicy = "never" - MigrationPolicyOnDemand MigrationPolicy = "on-demand" -) - -// ChangeTypeOptsBuilder allows extensions to add additional parameters to the -// ChangeType request. -type ChangeTypeOptsBuilder interface { - ToVolumeChangeTypeMap() (map[string]interface{}, error) -} - -// ChangeTypeOpts contains options for changing the type of an existing Volume. -// This object is passed to the volumes.ChangeType function. -type ChangeTypeOpts struct { - // NewType is the name of the new volume type of the volume. - NewType string `json:"new_type" required:"true"` - - // MigrationPolicy specifies if the volume should be migrated when it is - // re-typed. Possible values are "on-demand" or "never". If not specified, - // the default is "never". - MigrationPolicy MigrationPolicy `json:"migration_policy,omitempty"` -} - -// ToVolumeChangeTypeMap assembles a request body based on the contents of an -// ChangeTypeOpts. -func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "os-retype") -} - -// ChangeType will change the volume type of the volume based on the provided information. -// This operation does not return a response body. -func ChangeType(client *golangsdk.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) { - b, err := opts.ToVolumeChangeTypeMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// ReImageOpts contains options for Re-image a volume. -type ReImageOpts struct { - // New image id - ImageID string `json:"image_id"` - // set true to re-image volumes in reserved state - ReImageReserved bool `json:"reimage_reserved"` -} - -// ToReImageMap assembles a request body based on the contents of a ReImageOpts. -func (opts ReImageOpts) ToReImageMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "os-reimage") -} - -// ReImage will re-image a volume based on the values in ReImageOpts -func ReImage(client *golangsdk.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) { - b, err := opts.ToReImageMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("volumes", id, "action"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} From 925608502e996b50fe1c18cea46b4f5d63bf2e26 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Fri, 4 Nov 2022 12:55:17 +0100 Subject: [PATCH 33/51] Create --- openstack/evs/v3/snapshots/Create.go | 48 ++++++++++++++++++++++++++ openstack/evs/v3/snapshots/requests.go | 39 --------------------- openstack/evs/v3/snapshots/results.go | 8 ----- 3 files changed, 48 insertions(+), 47 deletions(-) create mode 100644 openstack/evs/v3/snapshots/Create.go diff --git a/openstack/evs/v3/snapshots/Create.go b/openstack/evs/v3/snapshots/Create.go new file mode 100644 index 000000000..55f4db09b --- /dev/null +++ b/openstack/evs/v3/snapshots/Create.go @@ -0,0 +1,48 @@ +package snapshots + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +// CreateOpts contains options for creating a Snapshot. This object is passed to +// the snapshots.Create function. For more information about these parameters, +// see the Snapshot object. +type CreateOpts struct { + // Specifies the ID of the snapshot's source disk. + VolumeID string `json:"volume_id" required:"true"` + // Specifies the flag for forcibly creating a snapshot. The default value is false. + // If this parameter is set to false and the disk is in the attaching state, the snapshot cannot be forcibly created. + // If this parameter is set to true and the disk is in the attaching state, the snapshot can be forcibly created. + Force bool `json:"force,omitempty"` + // Specifies the snapshot name. The value can contain a maximum of 255 bytes. + // NOTE + // When creating a backup for a disk, a snapshot will be created and named with prefix autobk_snapshot_. + // The EVS console has imposed operation restrictions on snapshots with prefix autobk_snapshot_. Therefore, + // you are advised not to use autobk_snapshot_ as the name prefix for the snapshots you created. + // Otherwise, the snapshots cannot be used normally. + Name string `json:"name,omitempty"` + // Specifies the snapshot description. The value can be null. The value can contain a maximum of 255 bytes. + Description string `json:"description,omitempty"` + // Specifies the snapshot metadata. + Metadata map[string]string `json:"metadata,omitempty"` +} + +// Create will create a new Snapshot based on the values in CreateOpts. To +// extract the Snapshot object from the response, call the Extract method on the +// CreateResult. +func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Snapshot, error) { + b, err := build.RequestBody(opts, "snapshot") + if err != nil { + return nil, err + } + + raw, err := client.Post(client.ServiceURL("snapshots"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{202}, + }) + + var res Snapshot + err = extract.IntoStructPtr(raw.Body, &res, "snapshot") + return &res, err +} diff --git a/openstack/evs/v3/snapshots/requests.go b/openstack/evs/v3/snapshots/requests.go index 94a7e761c..cbec63147 100644 --- a/openstack/evs/v3/snapshots/requests.go +++ b/openstack/evs/v3/snapshots/requests.go @@ -5,45 +5,6 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToSnapshotCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Snapshot. This object is passed to -// the snapshots.Create function. For more information about these parameters, -// see the Snapshot object. -type CreateOpts struct { - VolumeID string `json:"volume_id" required:"true"` - Force bool `json:"force,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// ToSnapshotCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "snapshot") -} - -// Create will create a new Snapshot based on the values in CreateOpts. To -// extract the Snapshot object from the response, call the Extract method on the -// CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToSnapshotCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("snapshots"), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - // Delete will delete the existing Snapshot with the provided ID. func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { resp, err := client.Delete(client.ServiceURL("snapshots", id), nil) diff --git a/openstack/evs/v3/snapshots/results.go b/openstack/evs/v3/snapshots/results.go index deddb4f47..fe12c6c50 100644 --- a/openstack/evs/v3/snapshots/results.go +++ b/openstack/evs/v3/snapshots/results.go @@ -12,28 +12,20 @@ import ( type Snapshot struct { // Unique identifier. ID string `json:"id"` - // Date created. CreatedAt time.Time `json:"-"` - // Date updated. UpdatedAt time.Time `json:"-"` - // Display name. Name string `json:"name"` - // Display description. Description string `json:"description"` - // ID of the Volume from which this Snapshot was created. VolumeID string `json:"volume_id"` - // Currect status of the Snapshot. Status string `json:"status"` - // Size of the Snapshot, in GB. Size int `json:"size"` - // User-defined key-value pairs. Metadata map[string]string `json:"metadata"` } From 26f15417a73a0112ab8d112b897e55b16c70bd19 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Fri, 4 Nov 2022 12:56:57 +0100 Subject: [PATCH 34/51] Delete --- openstack/evs/v3/snapshots/Create.go | 8 +++++--- openstack/evs/v3/snapshots/Delete.go | 10 ++++++++++ openstack/evs/v3/snapshots/requests.go | 7 ------- 3 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 openstack/evs/v3/snapshots/Delete.go diff --git a/openstack/evs/v3/snapshots/Create.go b/openstack/evs/v3/snapshots/Create.go index 55f4db09b..0da998af3 100644 --- a/openstack/evs/v3/snapshots/Create.go +++ b/openstack/evs/v3/snapshots/Create.go @@ -38,9 +38,11 @@ func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Snapshot, error) return nil, err } - raw, err := client.Post(client.ServiceURL("snapshots"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) + // POST /v3/{project_id}/snapshots + raw, err := client.Post(client.ServiceURL("snapshots"), b, nil, nil) + if err != nil { + return nil, err + } var res Snapshot err = extract.IntoStructPtr(raw.Body, &res, "snapshot") diff --git a/openstack/evs/v3/snapshots/Delete.go b/openstack/evs/v3/snapshots/Delete.go new file mode 100644 index 000000000..405b214e1 --- /dev/null +++ b/openstack/evs/v3/snapshots/Delete.go @@ -0,0 +1,10 @@ +package snapshots + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// Delete will delete the existing Snapshot with the provided ID. +func Delete(client *golangsdk.ServiceClient, id string) (err error) { + // DELETE /v3/{project_id}/snapshots/{snapshot_id} + _, err = client.Delete(client.ServiceURL("snapshots", id), nil) + return +} diff --git a/openstack/evs/v3/snapshots/requests.go b/openstack/evs/v3/snapshots/requests.go index cbec63147..1fc43d119 100644 --- a/openstack/evs/v3/snapshots/requests.go +++ b/openstack/evs/v3/snapshots/requests.go @@ -5,13 +5,6 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) -// Delete will delete the existing Snapshot with the provided ID. -func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(client.ServiceURL("snapshots", id), nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { From 828854acf765edec6e300afa30ed21b8e91431d4 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Fri, 4 Nov 2022 13:18:00 +0100 Subject: [PATCH 35/51] Update --- openstack/evs/v3/snapshots/Create.go | 9 +------ openstack/evs/v3/snapshots/Update.go | 36 ++++++++++++++++++++++++++ openstack/evs/v3/snapshots/requests.go | 35 ------------------------- openstack/evs/v3/snapshots/results.go | 15 ++++++----- 4 files changed, 46 insertions(+), 49 deletions(-) create mode 100644 openstack/evs/v3/snapshots/Update.go diff --git a/openstack/evs/v3/snapshots/Create.go b/openstack/evs/v3/snapshots/Create.go index 0da998af3..bb4fc2f30 100644 --- a/openstack/evs/v3/snapshots/Create.go +++ b/openstack/evs/v3/snapshots/Create.go @@ -3,7 +3,6 @@ package snapshots import ( "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/internal/build" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" ) // CreateOpts contains options for creating a Snapshot. This object is passed to @@ -40,11 +39,5 @@ func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Snapshot, error) // POST /v3/{project_id}/snapshots raw, err := client.Post(client.ServiceURL("snapshots"), b, nil, nil) - if err != nil { - return nil, err - } - - var res Snapshot - err = extract.IntoStructPtr(raw.Body, &res, "snapshot") - return &res, err + return extra(err, raw) } diff --git a/openstack/evs/v3/snapshots/Update.go b/openstack/evs/v3/snapshots/Update.go new file mode 100644 index 000000000..44888b0fe --- /dev/null +++ b/openstack/evs/v3/snapshots/Update.go @@ -0,0 +1,36 @@ +package snapshots + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) + +// UpdateOpts contain options for updating an existing Snapshot. This object is passed +// to the snapshots.Update function. For more information about the parameters, see +// the Snapshot object. +type UpdateOpts struct { + // Specifies the snapshot name. The value can contain a maximum of 255 bytes. + // NOTE + // When creating a backup for a disk, a snapshot will be created and named with prefix autobk_snapshot_. + // The EVS console has imposed operation restrictions on snapshots with prefix autobk_snapshot_. + // Therefore, you are advised not to use autobk_snapshot_ as the name prefix for the snapshots you created. + // Otherwise, the snapshots cannot be used normally. + Name string `json:"name,omitempty"` + // Specifies the snapshot description. The value can contain a maximum of 255 bytes. + Description string `json:"description,omitempty"` +} + +// Update will update the Snapshot with provided information. To extract the updated +// Snapshot from the response, call the Extract method on the UpdateResult. +func Update(client *golangsdk.ServiceClient, id string, opts UpdateOpts) (*Snapshot, error) { + b, err := build.RequestBody(opts, "snapshot") + if err != nil { + return nil, err + } + + // PUT /v3/{project_id}/snapshots/{snapshot_id} + raw, err := client.Put(client.ServiceURL("snapshots", id), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return extra(err, raw) +} diff --git a/openstack/evs/v3/snapshots/requests.go b/openstack/evs/v3/snapshots/requests.go index 1fc43d119..c47e242b9 100644 --- a/openstack/evs/v3/snapshots/requests.go +++ b/openstack/evs/v3/snapshots/requests.go @@ -74,41 +74,6 @@ func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Page }) } -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToSnapshotUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contain options for updating an existing Snapshot. This object is passed -// to the snapshots.Update function. For more information about the parameters, see -// the Snapshot object. -type UpdateOpts struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` -} - -// ToSnapshotUpdateMap assembles a request body based on the contents of an -// UpdateOpts. -func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "snapshot") -} - -// Update will update the Snapshot with provided information. To extract the updated -// Snapshot from the response, call the Extract method on the UpdateResult. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToSnapshotUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(client.ServiceURL("snapshots", id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - // UpdateMetadataOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateMetadataOptsBuilder interface { diff --git a/openstack/evs/v3/snapshots/results.go b/openstack/evs/v3/snapshots/results.go index fe12c6c50..ca56853cb 100644 --- a/openstack/evs/v3/snapshots/results.go +++ b/openstack/evs/v3/snapshots/results.go @@ -2,9 +2,11 @@ package snapshots import ( "encoding/json" + "net/http" "time" "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) @@ -121,11 +123,12 @@ type commonResult struct { golangsdk.Result } -// Extract will get the Snapshot object out of the commonResult object. -func (r commonResult) Extract() (*Snapshot, error) { - var s struct { - Snapshot *Snapshot `json:"snapshot"` +func extra(err error, raw *http.Response) (*Snapshot, error) { + if err != nil { + return nil, err } - err := r.ExtractInto(&s) - return s.Snapshot, err + + var res Snapshot + err = extract.IntoStructPtr(raw.Body, &res, "snapshot") + return &res, err } From 00b9bd17461ce581ce2f41e3f70c72fae43f5692 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Fri, 4 Nov 2022 13:27:00 +0100 Subject: [PATCH 36/51] List --- openstack/evs/v3/snapshots/List.go | 87 ++++++++++++++++++++++++++ openstack/evs/v3/snapshots/requests.go | 62 ------------------ openstack/evs/v3/snapshots/results.go | 34 ---------- 3 files changed, 87 insertions(+), 96 deletions(-) create mode 100644 openstack/evs/v3/snapshots/List.go diff --git a/openstack/evs/v3/snapshots/List.go b/openstack/evs/v3/snapshots/List.go new file mode 100644 index 000000000..35bbf7e90 --- /dev/null +++ b/openstack/evs/v3/snapshots/List.go @@ -0,0 +1,87 @@ +package snapshots + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" + "github.com/opentelekomcloud/gophertelekomcloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the List request. +type ListOptsBuilder interface { + ToSnapshotListQuery() (string, error) +} + +// ListOpts holds options for listing Snapshots. It is passed to the snapshots.List function. +type ListOpts struct { + // AllTenants will retrieve snapshots of all tenants/projects. + AllTenants bool `q:"all_tenants"` + // Name will filter by the specified snapshot name. + Name string `q:"name"` + // Status will filter by the specified status. + Status string `q:"status"` + // VolumeID will filter by a specified volume ID. + VolumeID string `q:"volume_id"` + // Specifies the result sorting order. The default value is desc. + // desc: indicates the descending order. + // asc: indicates the ascending order. + Sort string `q:"sort_dir"` + // Specifies the sorting query by name (sort_key=name). + // This parameter is supported when the request version is 3.30 or later. The default sorting order is the descending order. + SortKey string `q:"sort_key"` + // Requests a page size of items. + Limit int `q:"limit"` + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + // Specifies the ID of the last record on the previous page. The returned value is the value of the item after this one. + Marker string `q:"marker"` + // Specifies to return parameter counts in the response. This parameter indicates the number of snapshots queried. + // This parameter is in the with_count=true format and is supported when the request version is 3.45 or later. + // This parameter can be used together with parameters marker, limit, sort_key, sort_dir, or offset in the table. + // It cannot be used with other filter parameters. + WithCount bool `q:"with_count"` + // Specifies the fuzzy search by disk name. This parameter is supported when the request version is 3.31 or later. + FuzzyName string `q:"name~"` + FuzzyStatus string `q:"status~"` + FuzzyVolumeID string `q:"volume_id~"` +} + +// List returns Snapshots optionally limited by the conditions provided in ListOpts. +func List(client *golangsdk.ServiceClient, opts ListOpts) pagination.Pager { + q, err := golangsdk.BuildQueryString(opts) + if err != nil { + return pagination.Pager{Err: err} + } + + return pagination.NewPager(client, client.ServiceURL("snapshots")+q.String(), func(r pagination.PageResult) pagination.Page { + return SnapshotPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// SnapshotPage is a pagination.Pager that is returned from a call to the List function. +type SnapshotPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a SnapshotPage contains no Snapshots. +func (r SnapshotPage) IsEmpty() (bool, error) { + volumes, err := ExtractSnapshots(r) + return len(volumes) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r SnapshotPage) NextPageURL() (string, error) { + var s []golangsdk.Link + err := extract.IntoSlicePtr(r.BodyReader(), &s, "snapshots_links") + if err != nil { + return "", err + } + return golangsdk.ExtractNextURL(s) +} + +// ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. +func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { + var s []Snapshot + err := extract.IntoSlicePtr(r.(SnapshotPage).BodyReader(), &s, "snapshots") + return s, err +} diff --git a/openstack/evs/v3/snapshots/requests.go b/openstack/evs/v3/snapshots/requests.go index c47e242b9..4ed4b1c5b 100644 --- a/openstack/evs/v3/snapshots/requests.go +++ b/openstack/evs/v3/snapshots/requests.go @@ -2,7 +2,6 @@ package snapshots import ( "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Get retrieves the Snapshot with the provided ID. To extract the Snapshot @@ -13,67 +12,6 @@ func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { return } -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToSnapshotListQuery() (string, error) -} - -// ListOpts holds options for listing Snapshots. It is passed to the snapshots.List -// function. -type ListOpts struct { - // AllTenants will retrieve snapshots of all tenants/projects. - AllTenants bool `q:"all_tenants"` - - // Name will filter by the specified snapshot name. - Name string `q:"name"` - - // Status will filter by the specified status. - Status string `q:"status"` - - // TenantID will filter by a specific tenant/project ID. - // Setting AllTenants is required to use this. - TenantID string `q:"project_id"` - - // VolumeID will filter by a specified volume ID. - VolumeID string `q:"volume_id"` - - // Comma-separated list of sort keys and optional sort directions in the - // form of [:]. - Sort string `q:"sort"` - - // Requests a page size of items. - Limit int `q:"limit"` - - // Used in conjunction with limit to return a slice of items. - Offset int `q:"offset"` - - // The ID of the last-seen item. - Marker string `q:"marker"` -} - -// ToSnapshotListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToSnapshotListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// List returns Snapshots optionally limited by the conditions provided in -// ListOpts. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := client.ServiceURL("snapshots") - if opts != nil { - query, err := opts.ToSnapshotListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return SnapshotPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - // UpdateMetadataOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateMetadataOptsBuilder interface { diff --git a/openstack/evs/v3/snapshots/results.go b/openstack/evs/v3/snapshots/results.go index ca56853cb..ce15ab8c0 100644 --- a/openstack/evs/v3/snapshots/results.go +++ b/openstack/evs/v3/snapshots/results.go @@ -7,7 +7,6 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Snapshot contains all the information associated with a Cinder Snapshot. @@ -52,11 +51,6 @@ type UpdateResult struct { commonResult } -// SnapshotPage is a pagination.Pager that is returned from a call to the List function. -type SnapshotPage struct { - pagination.LinkedPageBase -} - // UnmarshalJSON converts our JSON API response into our snapshot struct func (r *Snapshot) UnmarshalJSON(b []byte) error { type tmp Snapshot @@ -77,34 +71,6 @@ func (r *Snapshot) UnmarshalJSON(b []byte) error { return err } -// IsEmpty returns true if a SnapshotPage contains no Snapshots. -func (r SnapshotPage) IsEmpty() (bool, error) { - volumes, err := ExtractSnapshots(r) - return len(volumes) == 0, err -} - -// NextPageURL uses the response's embedded link reference to navigate to the -// next page of results. -func (r SnapshotPage) NextPageURL() (string, error) { - var s struct { - Links []golangsdk.Link `json:"snapshots_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return golangsdk.ExtractNextURL(s.Links) -} - -// ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. -func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { - var s struct { - Snapshots []Snapshot `json:"snapshots"` - } - err := (r.(SnapshotPage)).ExtractInto(&s) - return s.Snapshots, err -} - // UpdateMetadataResult contains the response body and error from an UpdateMetadata request. type UpdateMetadataResult struct { commonResult From 3fe9730e5e42b55ccd9f84c761bbca2d3ae7c78a Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Fri, 4 Nov 2022 13:32:39 +0100 Subject: [PATCH 37/51] metadata unf --- openstack/evs/v3/snapshots/List.go | 1 + openstack/evs/v3/snapshots/metadata/Create.go | 34 +++++++++++++++++++ openstack/evs/v3/snapshots/metadata/Delete.go | 11 ++++++ openstack/evs/v3/snapshots/metadata/Get.go | 9 +++++ openstack/evs/v3/snapshots/metadata/GetOne.go | 26 ++++++++++++++ openstack/evs/v3/snapshots/metadata/Update.go | 19 +++++++++++ .../evs/v3/snapshots/metadata/UpdateOne.go | 19 +++++++++++ openstack/evs/v3/volumes/List.go | 8 ++--- openstack/evs/v3/volumetypes/List.go | 9 ++--- 9 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 openstack/evs/v3/snapshots/metadata/Create.go create mode 100644 openstack/evs/v3/snapshots/metadata/Delete.go create mode 100644 openstack/evs/v3/snapshots/metadata/Get.go create mode 100644 openstack/evs/v3/snapshots/metadata/GetOne.go create mode 100644 openstack/evs/v3/snapshots/metadata/Update.go create mode 100644 openstack/evs/v3/snapshots/metadata/UpdateOne.go diff --git a/openstack/evs/v3/snapshots/List.go b/openstack/evs/v3/snapshots/List.go index 35bbf7e90..045300bfc 100644 --- a/openstack/evs/v3/snapshots/List.go +++ b/openstack/evs/v3/snapshots/List.go @@ -52,6 +52,7 @@ func List(client *golangsdk.ServiceClient, opts ListOpts) pagination.Pager { return pagination.Pager{Err: err} } + // GET /v3/{project_id}/snapshots return pagination.NewPager(client, client.ServiceURL("snapshots")+q.String(), func(r pagination.PageResult) pagination.Page { return SnapshotPage{pagination.LinkedPageBase{PageResult: r}} }) diff --git a/openstack/evs/v3/snapshots/metadata/Create.go b/openstack/evs/v3/snapshots/metadata/Create.go new file mode 100644 index 000000000..51968f42e --- /dev/null +++ b/openstack/evs/v3/snapshots/metadata/Create.go @@ -0,0 +1,34 @@ +package metadata + +import ( + "net/http" + + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +func Create(client *golangsdk.ServiceClient, snapshotId string, opts map[string]string) (map[string]string, error) { + b, err := build.RequestBody(opts, "metadata") + if err != nil { + return nil, err + } + + // POST /v3/{project_id}/snapshots/{snapshot_id}/metadata + raw, err := client.Post(client.ServiceURL("snapshots", snapshotId, "metadata"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return extraMetadata(err, raw) +} + +func extraMetadata(err error, raw *http.Response) (map[string]string, error) { + if err != nil { + return nil, err + } + + var res struct { + Metadata map[string]string `json:"metadata"` + } + err = extract.Into(raw.Body, &res) + return res.Metadata, err +} diff --git a/openstack/evs/v3/snapshots/metadata/Delete.go b/openstack/evs/v3/snapshots/metadata/Delete.go new file mode 100644 index 000000000..31c7479de --- /dev/null +++ b/openstack/evs/v3/snapshots/metadata/Delete.go @@ -0,0 +1,11 @@ +package metadata + +import golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + +func Delete(client *golangsdk.ServiceClient, volumeId string, key string) (err error) { + // DELETE /v3/{project_id}/volumes/{volume_id}/metadata/{key} + _, err = client.Delete(client.ServiceURL("volumes", volumeId, "metadata", key), &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/evs/v3/snapshots/metadata/Get.go b/openstack/evs/v3/snapshots/metadata/Get.go new file mode 100644 index 000000000..fd86dba01 --- /dev/null +++ b/openstack/evs/v3/snapshots/metadata/Get.go @@ -0,0 +1,9 @@ +package metadata + +import golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + +func Get(client *golangsdk.ServiceClient, volumeId string) (map[string]string, error) { + // GET /v3/{project_id}/volumes/{volume_id}/metadata + raw, err := client.Get(client.ServiceURL("volumes", volumeId, "metadata"), nil, nil) + return extraMetadata(err, raw) +} diff --git a/openstack/evs/v3/snapshots/metadata/GetOne.go b/openstack/evs/v3/snapshots/metadata/GetOne.go new file mode 100644 index 000000000..1eb3a5462 --- /dev/null +++ b/openstack/evs/v3/snapshots/metadata/GetOne.go @@ -0,0 +1,26 @@ +package metadata + +import ( + "net/http" + + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) + +func GetOne(client *golangsdk.ServiceClient, volumeId string, key string) (map[string]string, error) { + // GET /v3/{project_id}/volumes/{volume_id}/metadata/{key} + raw, err := client.Get(client.ServiceURL("volumes", volumeId, "metadata", key), nil, nil) + return extraMeta(err, raw) +} + +func extraMeta(err error, raw *http.Response) (map[string]string, error) { + if err != nil { + return nil, err + } + + var res struct { + Metadata map[string]string `json:"meta"` + } + err = extract.Into(raw.Body, &res) + return res.Metadata, err +} diff --git a/openstack/evs/v3/snapshots/metadata/Update.go b/openstack/evs/v3/snapshots/metadata/Update.go new file mode 100644 index 000000000..cfefb04e0 --- /dev/null +++ b/openstack/evs/v3/snapshots/metadata/Update.go @@ -0,0 +1,19 @@ +package metadata + +import ( + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) + +func Update(client *golangsdk.ServiceClient, volumeId string, opts map[string]string) (map[string]string, error) { + b, err := build.RequestBody(opts, "metadata") + if err != nil { + return nil, err + } + + // PUT /v3/{project_id}/volumes/{volume_id}/metadata + raw, err := client.Put(client.ServiceURL("volumes", volumeId, "metadata"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return extraMetadata(err, raw) +} diff --git a/openstack/evs/v3/snapshots/metadata/UpdateOne.go b/openstack/evs/v3/snapshots/metadata/UpdateOne.go new file mode 100644 index 000000000..94559bc48 --- /dev/null +++ b/openstack/evs/v3/snapshots/metadata/UpdateOne.go @@ -0,0 +1,19 @@ +package metadata + +import ( + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) + +func UpdateOne(client *golangsdk.ServiceClient, volumeId string, key string, opts map[string]string) (map[string]string, error) { + b, err := build.RequestBody(opts, "meta") + if err != nil { + return nil, err + } + + // PUT /v3/{project_id}/volumes/{volume_id}/metadata/{key} + raw, err := client.Put(client.ServiceURL("volumes", volumeId, "metadata", key), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return extraMeta(err, raw) +} diff --git a/openstack/evs/v3/volumes/List.go b/openstack/evs/v3/volumes/List.go index 725dac15f..ebd8dbc9e 100644 --- a/openstack/evs/v3/volumes/List.go +++ b/openstack/evs/v3/volumes/List.go @@ -65,14 +65,12 @@ func (r VolumePage) IsEmpty() (bool, error) { } func (r VolumePage) NextPageURL() (string, error) { - var s struct { - Links []golangsdk.Link `json:"volumes_links"` - } - err := extract.Into(r.BodyReader(), &s) + var s []golangsdk.Link + err := extract.IntoSlicePtr(r.BodyReader(), &s, "volumes_links") if err != nil { return "", err } - return golangsdk.ExtractNextURL(s.Links) + return golangsdk.ExtractNextURL(s) } // ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. diff --git a/openstack/evs/v3/volumetypes/List.go b/openstack/evs/v3/volumetypes/List.go index fcb203151..d0c2c950c 100644 --- a/openstack/evs/v3/volumetypes/List.go +++ b/openstack/evs/v3/volumetypes/List.go @@ -49,15 +49,12 @@ func (r VolumeTypePage) IsEmpty() (bool, error) { } func (r VolumeTypePage) NextPageURL() (string, error) { - var s struct { - Links []golangsdk.Link `json:"volume_type_links"` - } - - err := extract.Into(r.BodyReader(), &s) + var s []golangsdk.Link + err := extract.IntoSlicePtr(r.BodyReader(), &s, "volume_type_links") if err != nil { return "", err } - return golangsdk.ExtractNextURL(s.Links) + return golangsdk.ExtractNextURL(s) } // ExtractVolumeTypesInto similar to ExtractInto but operates on a `list` of volume types From 4b4036e3b4473a92cfd9ce498878dee2d44aec72 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Mon, 7 Nov 2022 14:50:52 +0100 Subject: [PATCH 38/51] Get --- openstack/evs/v3/snapshots/metadata/Get.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openstack/evs/v3/snapshots/metadata/Get.go b/openstack/evs/v3/snapshots/metadata/Get.go index fd86dba01..b9bb39dc1 100644 --- a/openstack/evs/v3/snapshots/metadata/Get.go +++ b/openstack/evs/v3/snapshots/metadata/Get.go @@ -2,8 +2,9 @@ package metadata import golangsdk "github.com/opentelekomcloud/gophertelekomcloud" -func Get(client *golangsdk.ServiceClient, volumeId string) (map[string]string, error) { - // GET /v3/{project_id}/volumes/{volume_id}/metadata - raw, err := client.Get(client.ServiceURL("volumes", volumeId, "metadata"), nil, nil) +// Get If metadata contains the __system__enableActive field, the snapshot is automatically created during the backup of a server. +func Get(client *golangsdk.ServiceClient, snapshotId string) (map[string]string, error) { + // GET /v3/{project_id}/snapshots/{snapshot_id}/metadata + raw, err := client.Get(client.ServiceURL("snapshots", snapshotId, "metadata"), nil, nil) return extraMetadata(err, raw) } From 4fbc771dae0e7e3d5e007ee8e014e5210df6908b Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Tue, 8 Nov 2022 14:00:21 +0100 Subject: [PATCH 39/51] snapshot_id --- openstack/evs/v3/snapshots/metadata/Delete.go | 6 +++--- openstack/evs/v3/snapshots/metadata/GetOne.go | 6 +++--- openstack/evs/v3/snapshots/metadata/Update.go | 6 +++--- openstack/evs/v3/snapshots/metadata/UpdateOne.go | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/openstack/evs/v3/snapshots/metadata/Delete.go b/openstack/evs/v3/snapshots/metadata/Delete.go index 31c7479de..e7cdfa8a9 100644 --- a/openstack/evs/v3/snapshots/metadata/Delete.go +++ b/openstack/evs/v3/snapshots/metadata/Delete.go @@ -2,9 +2,9 @@ package metadata import golangsdk "github.com/opentelekomcloud/gophertelekomcloud" -func Delete(client *golangsdk.ServiceClient, volumeId string, key string) (err error) { - // DELETE /v3/{project_id}/volumes/{volume_id}/metadata/{key} - _, err = client.Delete(client.ServiceURL("volumes", volumeId, "metadata", key), &golangsdk.RequestOpts{ +func Delete(client *golangsdk.ServiceClient, snapshotId string, key string) (err error) { + // DELETE /v3/{project_id}/snapshots/{snapshot_id}/metadata/{key} + _, err = client.Delete(client.ServiceURL("snapshots", snapshotId, "metadata", key), &golangsdk.RequestOpts{ OkCodes: []int{200}, }) return diff --git a/openstack/evs/v3/snapshots/metadata/GetOne.go b/openstack/evs/v3/snapshots/metadata/GetOne.go index 1eb3a5462..a1acb5214 100644 --- a/openstack/evs/v3/snapshots/metadata/GetOne.go +++ b/openstack/evs/v3/snapshots/metadata/GetOne.go @@ -7,9 +7,9 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" ) -func GetOne(client *golangsdk.ServiceClient, volumeId string, key string) (map[string]string, error) { - // GET /v3/{project_id}/volumes/{volume_id}/metadata/{key} - raw, err := client.Get(client.ServiceURL("volumes", volumeId, "metadata", key), nil, nil) +func GetOne(client *golangsdk.ServiceClient, snapshotId string, key string) (map[string]string, error) { + // GET /v3/{project_id}/snapshots/{snapshot_id}/metadata/{key} + raw, err := client.Get(client.ServiceURL("snapshots", snapshotId, "metadata", key), nil, nil) return extraMeta(err, raw) } diff --git a/openstack/evs/v3/snapshots/metadata/Update.go b/openstack/evs/v3/snapshots/metadata/Update.go index cfefb04e0..ca3c46bbd 100644 --- a/openstack/evs/v3/snapshots/metadata/Update.go +++ b/openstack/evs/v3/snapshots/metadata/Update.go @@ -5,14 +5,14 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/internal/build" ) -func Update(client *golangsdk.ServiceClient, volumeId string, opts map[string]string) (map[string]string, error) { +func Update(client *golangsdk.ServiceClient, snapshotId string, opts map[string]string) (map[string]string, error) { b, err := build.RequestBody(opts, "metadata") if err != nil { return nil, err } - // PUT /v3/{project_id}/volumes/{volume_id}/metadata - raw, err := client.Put(client.ServiceURL("volumes", volumeId, "metadata"), b, nil, &golangsdk.RequestOpts{ + // PUT /v3/{project_id}/snapshots/{snapshot_id}/metadata + raw, err := client.Put(client.ServiceURL("snapshots", snapshotId, "metadata"), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) return extraMetadata(err, raw) diff --git a/openstack/evs/v3/snapshots/metadata/UpdateOne.go b/openstack/evs/v3/snapshots/metadata/UpdateOne.go index 94559bc48..30118796d 100644 --- a/openstack/evs/v3/snapshots/metadata/UpdateOne.go +++ b/openstack/evs/v3/snapshots/metadata/UpdateOne.go @@ -5,14 +5,14 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/internal/build" ) -func UpdateOne(client *golangsdk.ServiceClient, volumeId string, key string, opts map[string]string) (map[string]string, error) { +func UpdateOne(client *golangsdk.ServiceClient, snapshotId string, key string, opts map[string]string) (map[string]string, error) { b, err := build.RequestBody(opts, "meta") if err != nil { return nil, err } - // PUT /v3/{project_id}/volumes/{volume_id}/metadata/{key} - raw, err := client.Put(client.ServiceURL("volumes", volumeId, "metadata", key), b, nil, &golangsdk.RequestOpts{ + // PUT /v3/{project_id}/snapshots/{snapshot_id}/metadata/{key} + raw, err := client.Put(client.ServiceURL("snapshots", snapshotId, "metadata", key), b, nil, &golangsdk.RequestOpts{ OkCodes: []int{200}, }) return extraMeta(err, raw) From 2a0f3e9f23195406f1af015bc0a7a21f2591ceb8 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Tue, 8 Nov 2022 14:29:09 +0100 Subject: [PATCH 40/51] Create --- openstack/evs/extensions/backups/doc.go | 124 ------------------ openstack/evs/extensions/limits/doc.go | 13 -- .../evs/extensions/schedulerhints/doc.go | 52 -------- openstack/evs/extensions/volumehost/doc.go | 26 ---- openstack/evs/extensions/volumetenants/doc.go | 26 ---- .../evs/extensions/volumetransfers/Create.go | 28 ++++ .../evs/extensions/volumetransfers/doc.go | 65 --------- .../extensions/volumetransfers/requests.go | 37 +----- .../evs/extensions/volumetransfers/results.go | 31 ++++- .../evs/extensions/volumetransfers/urls.go | 23 ---- 10 files changed, 57 insertions(+), 368 deletions(-) delete mode 100644 openstack/evs/extensions/backups/doc.go delete mode 100644 openstack/evs/extensions/limits/doc.go delete mode 100644 openstack/evs/extensions/schedulerhints/doc.go delete mode 100644 openstack/evs/extensions/volumehost/doc.go delete mode 100644 openstack/evs/extensions/volumetenants/doc.go create mode 100644 openstack/evs/extensions/volumetransfers/Create.go delete mode 100644 openstack/evs/extensions/volumetransfers/doc.go delete mode 100644 openstack/evs/extensions/volumetransfers/urls.go diff --git a/openstack/evs/extensions/backups/doc.go b/openstack/evs/extensions/backups/doc.go deleted file mode 100644 index f96e6d3de..000000000 --- a/openstack/evs/extensions/backups/doc.go +++ /dev/null @@ -1,124 +0,0 @@ -/* -Package backups provides information and interaction with backups in the -OpenStack Block Storage service. A backup is a point in time copy of the -data contained in an external storage volume, and can be controlled -programmatically. - -Example to List Backups - - listOpts := backups.ListOpts{ - VolumeID: "uuid", - } - - allPages, err := backups.List(client, listOpts).AllPages() - if err != nil { - panic(err) - } - - allBackups, err := backups.ExtractBackups(allPages) - if err != nil { - panic(err) - } - - for _, backup := range allBackups { - fmt.Println(backup) - } - -Example to Create a Backup - - createOpts := backups.CreateOpts{ - VolumeID: "uuid", - Name: "my-backup", - } - - backup, err := backups.Create(client, createOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Println(backup) - -Example to Update a Backup - - updateOpts := backups.UpdateOpts{ - Name: "new-name", - } - - backup, err := backups.Update(client, "uuid", updateOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Println(backup) - -Example to Restore a Backup to a Volume - - options := backups.RestoreOpts{ - VolumeID: "1234", - Name: "vol-001", - } - - restore, err := backups.RestoreFromBackup(client, "uuid", options).Extract() - if err != nil { - panic(err) - } - - fmt.Println(restore) - -Example to Delete a Backup - - err := backups.Delete(client, "uuid").ExtractErr() - if err != nil { - panic(err) - } - -Example to Export a Backup - - export, err := backups.Export(client, "uuid").Extract() - if err != nil { - panic(err) - } - - fmt.Println(export) - -Example to Import a Backup - - status := "available" - availabilityZone := "region1b" - host := "cinder-backup-host1" - serviceMetadata := "volume_cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959/20200311192855/az_regionb_backup_b87bb1e5-0d4e-445e-a548-5ae742562bac" - size := 1 - objectCount := 2 - container := "my-test-backup" - service := "cinder.backup.drivers.swift.SwiftBackupDriver" - backupURL, _ := json.Marshal(backups.ImportBackup{ - ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", - Status: &status, - AvailabilityZone: &availabilityZone, - VolumeID: "cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959", - UpdatedAt: time.Date(2020, 3, 11, 19, 29, 8, 0, time.UTC), - Host: &host, - UserID: "93514e04-a026-4f60-8176-395c859501dd", - ServiceMetadata: &serviceMetadata, - Size: &size, - ObjectCount: &objectCount, - Container: &container, - Service: &service, - CreatedAt: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), - DataTimestamp: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), - ProjectID: "14f1c1f5d12b4755b94edef78ff8b325", - }) - - options := backups.ImportOpts{ - BackupService: "cinder.backup.drivers.swift.SwiftBackupDriver", - BackupURL: backupURL, - } - - backup, err := backups.Import(client, options).Extract() - if err != nil { - panic(err) - } - - fmt.Println(backup) -*/ -package backups diff --git a/openstack/evs/extensions/limits/doc.go b/openstack/evs/extensions/limits/doc.go deleted file mode 100644 index d33105884..000000000 --- a/openstack/evs/extensions/limits/doc.go +++ /dev/null @@ -1,13 +0,0 @@ -/* -Package limits shows rate and limit information for a project you authorized for. - -Example to Retrieve Limits - - limits, err := limits.Get(blockStorageClient).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", limits) -*/ -package limits diff --git a/openstack/evs/extensions/schedulerhints/doc.go b/openstack/evs/extensions/schedulerhints/doc.go deleted file mode 100644 index 3a0d451b6..000000000 --- a/openstack/evs/extensions/schedulerhints/doc.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Package schedulerhints extends the volume create request with the ability to -specify additional parameters which determine where the volume will be -created in the OpenStack cloud. - -Example to Place Volume B on a Different Host than Volume A - - schedulerHints := schedulerhints.SchedulerHints{ - DifferentHost: []string{ - "volume-a-uuid", - } - } - - volumeCreateOpts := volumes.CreateOpts{ - Name: "volume_b", - Size: 10, - } - - createOpts := schedulerhints.CreateOptsExt{ - VolumeCreateOptsBuilder: volumeCreateOpts, - SchedulerHints: schedulerHints, - } - - volume, err := volumes.Create(computeClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Place Volume B on the Same Host as Volume A - - schedulerHints := schedulerhints.SchedulerHints{ - SameHost: []string{ - "volume-a-uuid", - } - } - - volumeCreateOpts := volumes.CreateOpts{ - Name: "volume_b", - Size: 10 - } - - createOpts := schedulerhints.CreateOptsExt{ - VolumeCreateOptsBuilder: volumeCreateOpts, - SchedulerHints: schedulerHints, - } - - volume, err := volumes.Create(computeClient, createOpts).Extract() - if err != nil { - panic(err) - } -*/ -package schedulerhints diff --git a/openstack/evs/extensions/volumehost/doc.go b/openstack/evs/extensions/volumehost/doc.go deleted file mode 100644 index 6a651a7c5..000000000 --- a/openstack/evs/extensions/volumehost/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Package volumehost provides the ability to extend a volume result with -information about the Openstack host holding the volume. Example: - - type VolumeWithHost struct { - volumes.Volume - volumehost.VolumeHostExt - } - - var allVolumes []VolumeWithHost - - allPages, err := volumes.List(client, nil).AllPages() - if err != nil { - panic("Unable to retrieve volumes: %s", err) - } - - err = volumes.ExtractVolumesInto(allPages, &allVolumes) - if err != nil { - panic("Unable to extract volumes: %s", err) - } - - for _, volume := range allVolumes { - fmt.Println(volume.Host) - } -*/ -package volumehost diff --git a/openstack/evs/extensions/volumetenants/doc.go b/openstack/evs/extensions/volumetenants/doc.go deleted file mode 100644 index 2f501649f..000000000 --- a/openstack/evs/extensions/volumetenants/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Package volumetenants provides the ability to extend a volume result with -tenant/project information. Example: - - type VolumeWithTenant struct { - volumes.Volume - volumetenants.VolumeTenantExt - } - - var allVolumes []VolumeWithTenant - - allPages, err := volumes.List(client, nil).AllPages() - if err != nil { - panic("Unable to retrieve volumes: %s", err) - } - - err = volumes.ExtractVolumesInto(allPages, &allVolumes) - if err != nil { - panic("Unable to extract volumes: %s", err) - } - - for _, volume := range allVolumes { - fmt.Println(volume.TenantID) - } -*/ -package volumetenants diff --git a/openstack/evs/extensions/volumetransfers/Create.go b/openstack/evs/extensions/volumetransfers/Create.go new file mode 100644 index 000000000..8f0f4b2e4 --- /dev/null +++ b/openstack/evs/extensions/volumetransfers/Create.go @@ -0,0 +1,28 @@ +package volumetransfers + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) + +// CreateOpts contains options for a Volume transfer. +type CreateOpts struct { + // The ID of the volume to transfer. + VolumeID string `json:"volume_id" required:"true"` + // Specifies the disk transfer name. The value can contain a maximum of 255 bytes. + Name string `json:"name,omitempty"` +} + +// Create will create a volume tranfer request based on the values in CreateOpts. +func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Transfer, error) { + b, err := build.RequestBody(opts, "transfer") + if err != nil { + return nil, err + } + + // POST /v3/{project_id}/os-volume-transfer + raw, err := client.Post(client.ServiceURL("os-volume-transfer"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{202}, + }) + return extra(err, raw) +} diff --git a/openstack/evs/extensions/volumetransfers/doc.go b/openstack/evs/extensions/volumetransfers/doc.go deleted file mode 100644 index fa1ddaa0c..000000000 --- a/openstack/evs/extensions/volumetransfers/doc.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Package volumetransfers provides an interaction with volume transfers in the -OpenStack Block Storage service. A volume transfer allows to transfer volumes -between projects withing the same OpenStack region. - -Example to List all Volume Transfer requests being an OpenStack admin - - listOpts := &volumetransfers.ListOpts{ - // this option is available only for OpenStack cloud admin - AllTenants: true, - } - - allPages, err := volumetransfers.List(client, listOpts).AllPages() - if err != nil { - panic(err) - } - - allTransfers, err := volumetransfers.ExtractTransfers(allPages) - if err != nil { - panic(err) - } - - for _, transfer := range allTransfers { - fmt.Println(transfer) - } - -Example to Create a Volume Transfer request - - createOpts := volumetransfers.CreateOpts{ - VolumeID: "uuid", - Name: "my-volume-transfer", - } - - transfer, err := volumetransfers.Create(client, createOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Println(transfer) - // secret auth key is returned only once as a create response - fmt.Printf("AuthKey: %s\n", transfer.AuthKey) - -Example to Accept a Volume Transfer request from the target project - - acceptOpts := volumetransfers.AcceptOpts{ - // see the create response above - AuthKey: "volume-transfer-secret-auth-key", - } - - // see the transfer ID from the create response above - transfer, err := volumetransfers.Accept(client, "transfer-uuid", acceptOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Println(transfer) - -Example to Delete a Volume Transfer request from the source project - - err := volumetransfers.Delete(client, "transfer-uuid").ExtractErr() - if err != nil { - panic(err) - } -*/ -package volumetransfers diff --git a/openstack/evs/extensions/volumetransfers/requests.go b/openstack/evs/extensions/volumetransfers/requests.go index 71c05507b..f249ae131 100644 --- a/openstack/evs/extensions/volumetransfers/requests.go +++ b/openstack/evs/extensions/volumetransfers/requests.go @@ -5,35 +5,6 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) -// CreateOpts contains options for a Volume transfer. -type CreateOpts struct { - // The ID of the volume to transfer. - VolumeID string `json:"volume_id" required:"true"` - - // The name of the volume transfer - Name string `json:"name,omitempty"` -} - -// ToCreateMap assembles a request body based on the contents of a -// TransferOpts. -func (opts CreateOpts) ToCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "transfer") -} - -// Create will create a volume tranfer request based on the values in CreateOpts. -func Create(client *golangsdk.ServiceClient, opts CreateOpts) (r CreateResult) { - b, err := opts.ToCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(transferURL(client), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - // AcceptOpts contains options for a Volume transfer accept reqeust. type AcceptOpts struct { // The auth key of the volume transfer to accept. @@ -53,7 +24,7 @@ func Accept(client *golangsdk.ServiceClient, id string, opts AcceptOpts) (r Crea r.Err = err return } - resp, err := client.Post(acceptURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ + resp, err := client.Post(client.ServiceURL("os-volume-transfer", id, "accept"), b, &r.Body, &golangsdk.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) @@ -62,7 +33,7 @@ func Accept(client *golangsdk.ServiceClient, id string, opts AcceptOpts) (r Crea // Delete deletes a volume transfer. func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(client.ServiceURL("os-volume-transfer", id), nil) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } @@ -101,7 +72,7 @@ func (opts ListOpts) ToTransferListQuery() (string, error) { // List returns Transfers optionally limited by the conditions provided in ListOpts. func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) + url := client.ServiceURL("os-volume-transfer", "detail") if opts != nil { query, err := opts.ToTransferListQuery() if err != nil { @@ -118,7 +89,7 @@ func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Page // Get retrieves the Transfer with the provided ID. To extract the Transfer object // from the response, call the Extract method on the GetResult. func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(client.ServiceURL("os-volume-transfer", id), &r.Body, nil) _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) return } diff --git a/openstack/evs/extensions/volumetransfers/results.go b/openstack/evs/extensions/volumetransfers/results.go index e5f7dc9ec..70480a9a4 100644 --- a/openstack/evs/extensions/volumetransfers/results.go +++ b/openstack/evs/extensions/volumetransfers/results.go @@ -2,20 +2,29 @@ package volumetransfers import ( "encoding/json" + "net/http" "time" "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Transfer represents a Volume Transfer record type Transfer struct { - ID string `json:"id"` - AuthKey string `json:"auth_key"` - Name string `json:"name"` - VolumeID string `json:"volume_id"` - CreatedAt time.Time `json:"-"` - Links []map[string]string `json:"links"` + // Specifies the disk transfer ID. + ID string `json:"id"` + // Specifies the authentication key of the disk transfer. + AuthKey string `json:"auth_key"` + // Specifies the disk transfer name. + Name string `json:"name"` + // Specifies the disk ID. + VolumeID string `json:"volume_id"` + // Specifies the time when the disk transfer was created. + // Time format: UTC YYYY-MM-DDTHH:MM:SS.XXXXXX + CreatedAt time.Time `json:"-"` + // Specifies the links of the disk transfer. + Links []map[string]string `json:"links"` } // UnmarshalJSON is our unmarshalling helper @@ -36,6 +45,16 @@ func (r *Transfer) UnmarshalJSON(b []byte) error { return err } +func extra(err error, raw *http.Response) (*Transfer, error) { + if err != nil { + return nil, err + } + + var res Transfer + err = extract.IntoStructPtr(raw.Body, &res, "transfer") + return &res, err +} + type commonResult struct { golangsdk.Result } diff --git a/openstack/evs/extensions/volumetransfers/urls.go b/openstack/evs/extensions/volumetransfers/urls.go deleted file mode 100644 index 472d12d9f..000000000 --- a/openstack/evs/extensions/volumetransfers/urls.go +++ /dev/null @@ -1,23 +0,0 @@ -package volumetransfers - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func transferURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("os-volume-transfer") -} - -func acceptURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("os-volume-transfer", id, "accept") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("os-volume-transfer", id) -} - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("os-volume-transfer", "detail") -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("os-volume-transfer", id) -} From ec201991d91606bc76d4d517a9fbddc24e4a8ee3 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Tue, 8 Nov 2022 14:41:09 +0100 Subject: [PATCH 41/51] Delete --- .../evs/extensions/volumetransfers/Accept.go | 27 +++++++++++++++ .../evs/extensions/volumetransfers/Delete.go | 10 ++++++ .../extensions/volumetransfers/requests.go | 33 ------------------- 3 files changed, 37 insertions(+), 33 deletions(-) create mode 100644 openstack/evs/extensions/volumetransfers/Accept.go create mode 100644 openstack/evs/extensions/volumetransfers/Delete.go diff --git a/openstack/evs/extensions/volumetransfers/Accept.go b/openstack/evs/extensions/volumetransfers/Accept.go new file mode 100644 index 000000000..a2b734dbb --- /dev/null +++ b/openstack/evs/extensions/volumetransfers/Accept.go @@ -0,0 +1,27 @@ +package volumetransfers + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/build" +) + +// AcceptOpts contains options for a Volume transfer accept reqeust. +type AcceptOpts struct { + // Specifies the authentication key of the disk transfer. + // Specifies the authentication key returned during the disk transfer creation. + AuthKey string `json:"auth_key" required:"true"` +} + +// Accept will accept a volume transfer request based on the values in AcceptOpts. +func Accept(client *golangsdk.ServiceClient, id string, opts AcceptOpts) (*Transfer, error) { + b, err := build.RequestBody(opts, "accept") + if err != nil { + return nil, err + } + + // POST /v3/{project_id}/os-volume-transfer/{transfer_id}/accept + raw, err := client.Post(client.ServiceURL("os-volume-transfer", id, "accept"), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{202}, + }) + return extra(err, raw) +} diff --git a/openstack/evs/extensions/volumetransfers/Delete.go b/openstack/evs/extensions/volumetransfers/Delete.go new file mode 100644 index 000000000..ebd9769d6 --- /dev/null +++ b/openstack/evs/extensions/volumetransfers/Delete.go @@ -0,0 +1,10 @@ +package volumetransfers + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// Delete deletes a volume transfer. +func Delete(client *golangsdk.ServiceClient, id string) (err error) { + // DELETE /v3/{project_id}/os-volume-transfer/{transfer_id} + _, err = client.Delete(client.ServiceURL("os-volume-transfer", id), nil) + return +} diff --git a/openstack/evs/extensions/volumetransfers/requests.go b/openstack/evs/extensions/volumetransfers/requests.go index f249ae131..90c60e95a 100644 --- a/openstack/evs/extensions/volumetransfers/requests.go +++ b/openstack/evs/extensions/volumetransfers/requests.go @@ -5,39 +5,6 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) -// AcceptOpts contains options for a Volume transfer accept reqeust. -type AcceptOpts struct { - // The auth key of the volume transfer to accept. - AuthKey string `json:"auth_key" required:"true"` -} - -// ToAcceptMap assembles a request body based on the contents of a -// AcceptOpts. -func (opts AcceptOpts) ToAcceptMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "accept") -} - -// Accept will accept a volume tranfer request based on the values in AcceptOpts. -func Accept(client *golangsdk.ServiceClient, id string, opts AcceptOpts) (r CreateResult) { - b, err := opts.ToAcceptMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("os-volume-transfer", id, "accept"), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Delete deletes a volume transfer. -func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(client.ServiceURL("os-volume-transfer", id), nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { From 503fe5ee3f75afcd587825c823ddb7d082e4c0d9 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Tue, 8 Nov 2022 14:47:13 +0100 Subject: [PATCH 42/51] Get --- .../evs/extensions/volumetransfers/Get.go | 13 +++++++++++ .../volumetransfers/{requests.go => List.go} | 22 ++++--------------- 2 files changed, 17 insertions(+), 18 deletions(-) create mode 100644 openstack/evs/extensions/volumetransfers/Get.go rename openstack/evs/extensions/volumetransfers/{requests.go => List.go} (74%) diff --git a/openstack/evs/extensions/volumetransfers/Get.go b/openstack/evs/extensions/volumetransfers/Get.go new file mode 100644 index 000000000..ff418a014 --- /dev/null +++ b/openstack/evs/extensions/volumetransfers/Get.go @@ -0,0 +1,13 @@ +package volumetransfers + +import ( + "github.com/opentelekomcloud/gophertelekomcloud" +) + +// Get retrieves the Transfer with the provided ID. To extract the Transfer object +// from the response, call the Extract method on the GetResult. +func Get(client *golangsdk.ServiceClient, id string) (*Transfer, error) { + // GET /v3/{project_id}/os-volume-transfer/{transfer_id} + raw, err := client.Get(client.ServiceURL("os-volume-transfer", id), nil, nil) + return extra(err, raw) +} diff --git a/openstack/evs/extensions/volumetransfers/requests.go b/openstack/evs/extensions/volumetransfers/List.go similarity index 74% rename from openstack/evs/extensions/volumetransfers/requests.go rename to openstack/evs/extensions/volumetransfers/List.go index 90c60e95a..01d0f7020 100644 --- a/openstack/evs/extensions/volumetransfers/requests.go +++ b/openstack/evs/extensions/volumetransfers/List.go @@ -16,17 +16,13 @@ type ListOptsBuilder interface { type ListOpts struct { // AllTenants will retrieve transfers of all tenants/projects. AllTenants bool `q:"all_tenants"` - // Comma-separated list of sort keys and optional sort directions in the // form of [:]. Sort string `q:"sort"` - // Requests a page size of items. Limit int `q:"limit"` - // Used in conjunction with limit to return a slice of items. Offset int `q:"offset"` - // The ID of the last-seen item. Marker string `q:"marker"` } @@ -39,24 +35,14 @@ func (opts ListOpts) ToTransferListQuery() (string, error) { // List returns Transfers optionally limited by the conditions provided in ListOpts. func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := client.ServiceURL("os-volume-transfer", "detail") - if opts != nil { - query, err := opts.ToTransferListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query + q, err := golangsdk.BuildQueryString(opts) + if err != nil { + return pagination.Pager{Err: err} } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return TransferPage{pagination.LinkedPageBase{PageResult: r}} }) } - -// Get retrieves the Transfer with the provided ID. To extract the Transfer object -// from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(client.ServiceURL("os-volume-transfer", id), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} From 85893aeafa81abe9f0ee5619d721606d24f00019 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Tue, 8 Nov 2022 14:58:06 +0100 Subject: [PATCH 43/51] List --- .../evs/extensions/volumetransfers/List.go | 45 +++++++++---- .../evs/extensions/volumetransfers/results.go | 66 ------------------- 2 files changed, 33 insertions(+), 78 deletions(-) diff --git a/openstack/evs/extensions/volumetransfers/List.go b/openstack/evs/extensions/volumetransfers/List.go index 01d0f7020..f3a4b1681 100644 --- a/openstack/evs/extensions/volumetransfers/List.go +++ b/openstack/evs/extensions/volumetransfers/List.go @@ -2,17 +2,11 @@ package volumetransfers import ( "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToTransferListQuery() (string, error) -} - -// ListOpts holds options for listing Transfers. It is passed to the transfers.List -// function. +// ListOpts holds options for listing Transfers. It is passed to the transfers.List function. type ListOpts struct { // AllTenants will retrieve transfers of all tenants/projects. AllTenants bool `q:"all_tenants"` @@ -34,15 +28,42 @@ func (opts ListOpts) ToTransferListQuery() (string, error) { } // List returns Transfers optionally limited by the conditions provided in ListOpts. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - - url := client.ServiceURL("os-volume-transfer", "detail") +func List(client *golangsdk.ServiceClient, opts ListOpts) pagination.Pager { q, err := golangsdk.BuildQueryString(opts) if err != nil { return pagination.Pager{Err: err} } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + // GET /v3/{project_id}/os-volume-transfer + return pagination.NewPager(client, client.ServiceURL("os-volume-transfer")+q.String(), func(r pagination.PageResult) pagination.Page { return TransferPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// TransferPage is a pagination.pager that is returned from a call to the List function. +type TransferPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a ListResult contains no Transfers. +func (r TransferPage) IsEmpty() (bool, error) { + transfers, err := ExtractTransfers(r) + return len(transfers) == 0, err +} + +func (r TransferPage) NextPageURL() (string, error) { + var res []golangsdk.Link + err := extract.IntoSlicePtr(r.BodyReader(), &res, "transfers_links") + if err != nil { + return "", err + } + + return golangsdk.ExtractNextURL(res) +} + +// ExtractTransfers extracts and returns Transfers. It is used while iterating over a transfers.List call. +func ExtractTransfers(r pagination.Page) ([]Transfer, error) { + var res []Transfer + err := extract.IntoSlicePtr(r.(TransferPage).Result.BodyReader(), &res, "transfers") + return res, err +} diff --git a/openstack/evs/extensions/volumetransfers/results.go b/openstack/evs/extensions/volumetransfers/results.go index 70480a9a4..5df5282c4 100644 --- a/openstack/evs/extensions/volumetransfers/results.go +++ b/openstack/evs/extensions/volumetransfers/results.go @@ -7,7 +7,6 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" ) // Transfer represents a Volume Transfer record @@ -54,68 +53,3 @@ func extra(err error, raw *http.Response) (*Transfer, error) { err = extract.IntoStructPtr(raw.Body, &res, "transfer") return &res, err } - -type commonResult struct { - golangsdk.Result -} - -// Extract will get the Transfer object out of the commonResult object. -func (r commonResult) Extract() (*Transfer, error) { - var s Transfer - err := r.ExtractInto(&s) - return &s, err -} - -// ExtractInto converts our response data into a transfer struct -func (r commonResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "transfer") -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - golangsdk.ErrResult -} - -// ExtractTransfers extracts and returns Transfers. It is used while iterating over a transfers.List call. -func ExtractTransfers(r pagination.Page) ([]Transfer, error) { - var s []Transfer - err := ExtractTransfersInto(r, &s) - return s, err -} - -// ExtractTransfersInto similar to ExtractInto but operates on a `list` of transfers -func ExtractTransfersInto(r pagination.Page, v interface{}) error { - return r.(TransferPage).Result.ExtractIntoSlicePtr(v, "transfers") -} - -// TransferPage is a pagination.pager that is returned from a call to the List function. -type TransferPage struct { - pagination.LinkedPageBase -} - -// IsEmpty returns true if a ListResult contains no Transfers. -func (r TransferPage) IsEmpty() (bool, error) { - transfers, err := ExtractTransfers(r) - return len(transfers) == 0, err -} - -func (page TransferPage) NextPageURL() (string, error) { - var s struct { - Links []golangsdk.Link `json:"transfers_links"` - } - err := page.ExtractInto(&s) - if err != nil { - return "", err - } - return golangsdk.ExtractNextURL(s.Links) -} From 0e30094a9db5efca2540307d5190ca6d385c14b1 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Tue, 8 Nov 2022 15:41:38 +0100 Subject: [PATCH 44/51] test --- acceptance/openstack/evs/v3/blockstorage.go | 70 +--- .../evs/v3/extensions/backups_test.go | 39 -- .../openstack/evs/v3/extensions/extensions.go | 149 +------- .../evs/v3/extensions/schedulerhints_test.go | 2 +- .../evs/v3/extensions/schedulerstats_test.go | 7 +- .../evs/v3/extensions/services_test.go | 9 +- .../evs/v3/extensions/volumeactions_test.go | 132 +------ .../evs/v3/extensions/volumetenants_test.go | 4 +- acceptance/openstack/evs/v3/qos_test.go | 127 ------- acceptance/openstack/evs/v3/quotaset_test.go | 116 +----- acceptance/openstack/evs/v3/snapshots_test.go | 14 +- .../openstack/evs/v3/volumeattachments.go | 110 ------ .../evs/v3/volumeattachments_test.go | 37 -- openstack/evs/extensions/backups/requests.go | 303 ---------------- openstack/evs/extensions/backups/results.go | 337 ------------------ openstack/evs/extensions/backups/urls.go | 39 -- openstack/evs/extensions/limits/requests.go | 13 - openstack/evs/extensions/limits/results.go | 80 ----- openstack/evs/extensions/limits/urls.go | 11 - openstack/evs/extensions/volumetenants.go | 6 - .../evs/v2/volumes/testing/requests_test.go | 6 +- openstack/evs/v3/attachments/requests.go | 180 ---------- openstack/evs/v3/attachments/results.go | 120 ------- openstack/evs/v3/attachments/util.go | 22 -- openstack/evs/v3/qos/requests.go | 328 ----------------- openstack/evs/v3/qos/results.go | 144 -------- openstack/evs/v3/snapshots/Create.go | 3 +- openstack/evs/v3/snapshots/Get.go | 11 + openstack/evs/v3/snapshots/requests.go | 48 --- openstack/evs/v3/snapshots/results.go | 55 +-- openstack/evs/v3/snapshots/util.go | 22 -- 31 files changed, 67 insertions(+), 2477 deletions(-) delete mode 100644 acceptance/openstack/evs/v3/extensions/backups_test.go delete mode 100644 acceptance/openstack/evs/v3/qos_test.go delete mode 100644 acceptance/openstack/evs/v3/volumeattachments.go delete mode 100644 acceptance/openstack/evs/v3/volumeattachments_test.go delete mode 100644 openstack/evs/extensions/backups/requests.go delete mode 100644 openstack/evs/extensions/backups/results.go delete mode 100644 openstack/evs/extensions/backups/urls.go delete mode 100644 openstack/evs/extensions/limits/requests.go delete mode 100644 openstack/evs/extensions/limits/results.go delete mode 100644 openstack/evs/extensions/limits/urls.go delete mode 100644 openstack/evs/extensions/volumetenants.go delete mode 100644 openstack/evs/v3/attachments/requests.go delete mode 100644 openstack/evs/v3/attachments/results.go delete mode 100644 openstack/evs/v3/attachments/util.go delete mode 100644 openstack/evs/v3/qos/requests.go delete mode 100644 openstack/evs/v3/qos/results.go create mode 100644 openstack/evs/v3/snapshots/Get.go delete mode 100644 openstack/evs/v3/snapshots/requests.go delete mode 100644 openstack/evs/v3/snapshots/util.go diff --git a/acceptance/openstack/evs/v3/blockstorage.go b/acceptance/openstack/evs/v3/blockstorage.go index 976e707a4..70d9fc88e 100644 --- a/acceptance/openstack/evs/v3/blockstorage.go +++ b/acceptance/openstack/evs/v3/blockstorage.go @@ -1,6 +1,3 @@ -// Package v3 contains common functions for creating block storage based -// resources for use in acceptance tests. See the `*_test.go` files for -// example usages. package v3 import ( @@ -8,7 +5,6 @@ import ( golangsdk "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/qos" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/snapshots" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumetypes" @@ -125,7 +121,7 @@ func DeleteSnapshot(t *testing.T, client *golangsdk.ServiceClient, snapshot *sna } // Volumes can't be deleted until their snapshots have been, - // so block until the snapshoth as been deleted. + // so block until the snapshot as been deleted. err = tools.WaitFor(func() (bool, error) { _, err := snapshots.Get(client, snapshot.ID) if err != nil { @@ -146,7 +142,9 @@ func DeleteSnapshot(t *testing.T, client *golangsdk.ServiceClient, snapshot *sna func DeleteVolume(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) { t.Logf("Attempting to delete volume: %s", volume.ID) - err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}) + err := volumes.Delete(client, volumes.DeleteOpts{ + VolumeId: volume.ID, + }) if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } @@ -167,63 +165,3 @@ func DeleteVolume(t *testing.T, client *golangsdk.ServiceClient, volume *volumes t.Logf("Successfully deleted volume: %s", volume.ID) } - -// DeleteVolumeType will delete a volume type. A fatal error will occur if the -// volume type failed to be deleted. This works best when used as a deferred -// function. -func DeleteVolumeType(t *testing.T, client *golangsdk.ServiceClient, vt *volumetypes.VolumeType) { - t.Logf("Attempting to delete volume type: %s", vt.ID) - - err := volumetypes.Delete(client, vt.ID) - if err != nil { - t.Fatalf("Unable to delete volume %s: %v", vt.ID, err) - } - - t.Logf("Successfully deleted volume type: %s", vt.ID) -} - -// CreateQoS will create a QoS with one spec and a random name. An -// error will be returned if the volume was unable to be created. -func CreateQoS(t *testing.T, client *golangsdk.ServiceClient) (*qos.QoS, error) { - name := tools.RandomString("ACPTTEST", 16) - t.Logf("Attempting to create QoS: %s", name) - - createOpts := qos.CreateOpts{ - Name: name, - Consumer: qos.ConsumerFront, - Specs: map[string]string{ - "read_iops_sec": "20000", - }, - } - - qs, err := qos.Create(client, createOpts) - if err != nil { - return nil, err - } - - tools.PrintResource(t, qs) - th.AssertEquals(t, qs.Consumer, "front-end") - th.AssertEquals(t, qs.Name, name) - th.AssertDeepEquals(t, qs.Specs, createOpts.Specs) - - t.Logf("Successfully created QoS: %s", qs.ID) - - return qs, nil -} - -// DeleteQoS will delete a QoS. A fatal error will occur if the QoS -// failed to be deleted. This works best when used as a deferred function. -func DeleteQoS(t *testing.T, client *golangsdk.ServiceClient, qs *qos.QoS) { - t.Logf("Attempting to delete QoS: %s", qs.ID) - - deleteOpts := qos.DeleteOpts{ - Force: true, - } - - err := qos.Delete(client, qs.ID, deleteOpts) - if err != nil { - t.Fatalf("Unable to delete QoS %s: %v", qs.ID, err) - } - - t.Logf("Successfully deleted QoS: %s", qs.ID) -} diff --git a/acceptance/openstack/evs/v3/extensions/backups_test.go b/acceptance/openstack/evs/v3/extensions/backups_test.go deleted file mode 100644 index ad8b433f5..000000000 --- a/acceptance/openstack/evs/v3/extensions/backups_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package extensions - -import ( - "testing" - - backups2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/backups" - - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" - blockstorage "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/evs/v3" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" -) - -func TestBackupsCRUD(t *testing.T) { - blockClient, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - volume, err := blockstorage.CreateVolume(t, blockClient) - th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) - - backup, err := CreateBackup(t, blockClient, volume.ID) - th.AssertNoErr(t, err) - defer DeleteBackup(t, blockClient, backup.ID) - - allPages, err := backups2.List(blockClient, nil).AllPages() - th.AssertNoErr(t, err) - - allBackups, err := backups2.ExtractBackups(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, v := range allBackups { - if backup.Name == v.Name { - found = true - } - } - - th.AssertEquals(t, found, true) -} diff --git a/acceptance/openstack/evs/v3/extensions/extensions.go b/acceptance/openstack/evs/v3/extensions/extensions.go index 2bb35c2bd..229c745b6 100644 --- a/acceptance/openstack/evs/v3/extensions/extensions.go +++ b/acceptance/openstack/evs/v3/extensions/extensions.go @@ -1,6 +1,3 @@ -// Package extensions contains common functions for creating block storage -// resources that are extensions of the block storage API. See the `*_test.go` -// files for example usages. package extensions import ( @@ -13,16 +10,13 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/images" "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/servers" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/volumeactions" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v2/volumes" - backups2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/backups" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" v3 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumetypes" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) -// CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be +// CreateUploadImage will upload volume it as volume-baked image. A name of new image or err will be // returned -func CreateUploadImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) (volumeactions.VolumeImage, error) { +func CreateUploadImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) (*volumeactions.VolumeImage, error) { if testing.Short() { t.Skip("Skipping test that requires volume-backed image uploading in short mode.") } @@ -59,7 +53,7 @@ func DeleteUploadedImage(t *testing.T, client *golangsdk.ServiceClient, imageID t.Logf("Removing image %s", imageID) - err := images.Delete(client, imageID) + err := images.Delete(client, imageID).ExtractErr() if err != nil { return err } @@ -171,85 +165,6 @@ func ExtendVolumeSize(t *testing.T, client *golangsdk.ServiceClient, volume *vol return nil } -// SetImageMetadata will apply the metadata to a volume. -func SetImageMetadata(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { - t.Logf("Attempting to apply image metadata to volume %s", volume.ID) - - imageMetadataOpts := volumeactions.ImageMetadataOpts{ - Metadata: map[string]string{ - "image_name": "testimage", - }, - } - - err := volumeactions.SetImageMetadata(client, volume.ID, imageMetadataOpts) - if err != nil { - return err - } - - return nil -} - -// CreateBackup will create a backup based on a volume. An error will be -// will be returned if the backup could not be created. -func CreateBackup(t *testing.T, client *golangsdk.ServiceClient, volumeID string) (*backups2.Backup, error) { - t.Logf("Attempting to create a backup of volume %s", volumeID) - - backupName := tools.RandomString("ACPTTEST", 16) - createOpts := backups2.CreateOpts{ - VolumeID: volumeID, - Name: backupName, - } - - backup, err := backups2.Create(client, createOpts) - if err != nil { - return nil, err - } - - err = WaitForBackupStatus(client, backup.ID, "available") - if err != nil { - return nil, err - } - - backup, err = backups2.Get(client, backup.ID) - if err != nil { - return nil, err - } - - t.Logf("Successfully created backup %s", backup.ID) - tools.PrintResource(t, backup) - - th.AssertEquals(t, backup.Name, backupName) - - return backup, nil -} - -// DeleteBackup will delete a backup. A fatal error will occur if the backup -// could not be deleted. This works best when used as a deferred function. -func DeleteBackup(t *testing.T, client *golangsdk.ServiceClient, backupID string) { - if err := backups2.Delete(client, backupID); err != nil { - t.Fatalf("Unable to delete backup %s: %s", backupID, err) - } - - t.Logf("Deleted backup %s", backupID) -} - -// WaitForBackupStatus will continually poll a backup, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForBackupStatus(client *golangsdk.ServiceClient, id, status string) error { - return tools.WaitFor(func() (bool, error) { - current, err := backups2.Get(client, id) - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} - // SetBootable will set a bootable status to a volume. func SetBootable(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume) error { t.Logf("Attempting to apply bootable status to volume %s", volume.ID) @@ -292,59 +207,3 @@ func SetBootable(t *testing.T, client *golangsdk.ServiceClient, volume *volumes. return nil } - -// ChangeVolumeType will extend the size of a volume. -func ChangeVolumeType(t *testing.T, client *golangsdk.ServiceClient, volume *v3.Volume, vt *volumetypes.VolumeType) error { - t.Logf("Attempting to change the type of volume %s from %s to %s", volume.ID, volume.VolumeType, vt.Name) - - changeOpts := volumeactions.ChangeTypeOpts{ - NewType: vt.Name, - MigrationPolicy: volumeactions.MigrationPolicyOnDemand, - } - - err := volumeactions.ChangeType(client, volume.ID, changeOpts) - if err != nil { - return err - } - - if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { - return err - } - - return nil -} - -// ReImage will re-image a volume -func ReImage(t *testing.T, client *golangsdk.ServiceClient, volume *volumes.Volume, imageID string) error { - t.Logf("Attempting to re-image volume %s", volume.ID) - - reimageOpts := volumeactions.ReImageOpts{ - ImageID: imageID, - ReImageReserved: false, - } - - err := volumeactions.ReImage(client, volume.ID, reimageOpts) - if err != nil { - return err - } - - err = volumes.WaitForStatus(client, volume.ID, "available", 60) - if err != nil { - return err - } - - vol, err := v3.Get(client, volume.ID) - if err != nil { - return err - } - - if vol.VolumeImageMetadata == nil { - return fmt.Errorf("volume does not have VolumeImageMetadata map") - } - - if strings.ToLower(vol.VolumeImageMetadata["image_id"]) != imageID { - return fmt.Errorf("volume image id '%s', expected '%s'", vol.VolumeImageMetadata["image_id"], imageID) - } - - return nil -} diff --git a/acceptance/openstack/evs/v3/extensions/schedulerhints_test.go b/acceptance/openstack/evs/v3/extensions/schedulerhints_test.go index ff646b2b2..58e1cfa53 100644 --- a/acceptance/openstack/evs/v3/extensions/schedulerhints_test.go +++ b/acceptance/openstack/evs/v3/extensions/schedulerhints_test.go @@ -3,7 +3,7 @@ package extensions import ( "testing" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/schedulerhints" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/schedulerhints" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" diff --git a/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go b/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go index 174906c5f..ba746fd7d 100644 --- a/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go +++ b/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go @@ -10,8 +10,6 @@ import ( ) func TestSchedulerStatsList(t *testing.T) { - clients.RequireAdmin(t) - blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) @@ -19,10 +17,7 @@ func TestSchedulerStatsList(t *testing.T) { Detail: true, } - allPages, err := schedulerstats.List(blockClient, listOpts).AllPages() - th.AssertNoErr(t, err) - - allStats, err := schedulerstats.ExtractStoragePools(allPages) + allStats, err := schedulerstats.List(blockClient, listOpts) th.AssertNoErr(t, err) for _, stat := range allStats { diff --git a/acceptance/openstack/evs/v3/extensions/services_test.go b/acceptance/openstack/evs/v3/extensions/services_test.go index 5cbe7ad53..ab923d8d6 100644 --- a/acceptance/openstack/evs/v3/extensions/services_test.go +++ b/acceptance/openstack/evs/v3/extensions/services_test.go @@ -3,7 +3,7 @@ package extensions import ( "testing" - services2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/services" + services2 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/services" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" @@ -11,15 +11,10 @@ import ( ) func TestServicesList(t *testing.T) { - clients.RequireAdmin(t) - blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - allPages, err := services2.List(blockClient, services2.ListOpts{}).AllPages() - th.AssertNoErr(t, err) - - allServices, err := services2.ExtractServices(allPages) + allServices, err := services2.List(blockClient, services2.ListOpts{}) th.AssertNoErr(t, err) for _, service := range allServices { diff --git a/acceptance/openstack/evs/v3/extensions/volumeactions_test.go b/acceptance/openstack/evs/v3/extensions/volumeactions_test.go index be5a0aea4..924543f4f 100644 --- a/acceptance/openstack/evs/v3/extensions/volumeactions_test.go +++ b/acceptance/openstack/evs/v3/extensions/volumeactions_test.go @@ -5,10 +5,9 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" compute "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/compute/v2" - blockstorage "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/evs/v2" blockstorageV3 "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/evs/v3" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v2/volumes" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) @@ -19,9 +18,9 @@ func TestVolumeActionsUploadImageDestroy(t *testing.T) { computeClient, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - volume, err := blockstorage.CreateVolume(t, blockClient) + volume, err := blockstorageV3.CreateVolume(t, blockClient) th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) + t.Cleanup(func() { blockstorageV3.DeleteVolume(t, blockClient, volume) }) volumeImage, err := CreateUploadImage(t, blockClient, volume) th.AssertNoErr(t, err) @@ -43,9 +42,9 @@ func TestVolumeActionsAttachCreateDestroy(t *testing.T) { th.AssertNoErr(t, err) defer compute.DeleteServer(t, computeClient, server) - volume, err := blockstorage.CreateVolume(t, blockClient) + volume, err := blockstorageV3.CreateVolume(t, blockClient) th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) + t.Cleanup(func() { blockstorageV3.DeleteVolume(t, blockClient, volume) }) err = CreateVolumeAttach(t, blockClient, volume, server) th.AssertNoErr(t, err) @@ -60,22 +59,22 @@ func TestVolumeActionsReserveUnreserve(t *testing.T) { client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - volume, err := blockstorage.CreateVolume(t, client) + volume, err := blockstorageV3.CreateVolume(t, client) th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, client, volume) + t.Cleanup(func() { blockstorageV3.DeleteVolume(t, client, volume) }) err = CreateVolumeReserve(t, client, volume) th.AssertNoErr(t, err) - defer DeleteVolumeReserve(t, client, volume) + t.Cleanup(func() { DeleteVolumeReserve(t, client, volume) }) } func TestVolumeActionsExtendSize(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - volume, err := blockstorage.CreateVolume(t, blockClient) + volume, err := blockstorageV3.CreateVolume(t, blockClient) th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) + t.Cleanup(func() { blockstorageV3.DeleteVolume(t, blockClient, volume) }) tools.PrintResource(t, volume) @@ -88,121 +87,14 @@ func TestVolumeActionsExtendSize(t *testing.T) { tools.PrintResource(t, newVolume) } -func TestVolumeActionsImageMetadata(t *testing.T) { - blockClient, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - volume, err := blockstorage.CreateVolume(t, blockClient) - th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) - - err = SetImageMetadata(t, blockClient, volume) - th.AssertNoErr(t, err) -} - func TestVolumeActionsSetBootable(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - volume, err := blockstorage.CreateVolume(t, blockClient) + volume, err := blockstorageV3.CreateVolume(t, blockClient) th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) + t.Cleanup(func() { blockstorageV3.DeleteVolume(t, blockClient, volume) }) err = SetBootable(t, blockClient, volume) th.AssertNoErr(t, err) } - -func TestVolumeActionsChangeType(t *testing.T) { - // clients.RequireAdmin(t) - - client, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - volumeType1, err := blockstorageV3.CreateVolumeTypeNoExtraSpecs(t, client) - th.AssertNoErr(t, err) - defer blockstorageV3.DeleteVolumeType(t, client, volumeType1) - - volumeType2, err := blockstorageV3.CreateVolumeTypeNoExtraSpecs(t, client) - th.AssertNoErr(t, err) - defer blockstorageV3.DeleteVolumeType(t, client, volumeType2) - - volume, err := blockstorageV3.CreateVolumeWithType(t, client, volumeType1) - th.AssertNoErr(t, err) - defer blockstorageV3.DeleteVolume(t, client, volume) - - tools.PrintResource(t, volume) - - err = ChangeVolumeType(t, client, volume, volumeType2) - th.AssertNoErr(t, err) - - newVolume, err := volumes.Get(client, volume.ID) - th.AssertNoErr(t, err) - th.AssertEquals(t, newVolume.VolumeType, volumeType2.Name) - - tools.PrintResource(t, newVolume) -} - -func TestVolumeActionsReImage(t *testing.T) { - clients.SkipReleasesBelow(t, "stable/yoga") - - choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } - - blockClient, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - blockClient.Microversion = "3.68" - - volume, err := blockstorage.CreateVolume(t, blockClient) - th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) - - err = ReImage(t, blockClient, volume, choices.ImageID) - th.AssertNoErr(t, err) -} - -// Note(jtopjian): I plan to work on this at some point, but it requires -// setting up a server with iscsi utils. -/* -func TestVolumeConns(t *testing.T) { - client, err := newClient() - th.AssertNoErr(t, err) - - t.Logf("Creating volume") - cv, err := volumes.Create(client, &volumes.CreateOpts{ - Size: 1, - Name: "blockv2-volume", - }) - th.AssertNoErr(t, err) - - defer func() { - err = volumes.WaitForStatus(client, cv.ID, "available", 60) - th.AssertNoErr(t, err) - - t.Logf("Deleting volume") - err = volumes.Delete(client, cv.ID, volumes.DeleteOpts{}) - th.AssertNoErr(t, err) - }() - - err = volumes.WaitForStatus(client, cv.ID, "available", 60) - th.AssertNoErr(t, err) - - connOpts := &volumeactions.ConnectorOpts{ - IP: "127.0.0.1", - Host: "stack", - Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: false, - Platform: "x86_64", - OSType: "linux2", - } - - t.Logf("Initializing connection") - _, err = volumeactions.InitializeConnection(client, cv.ID, connOpts) - th.AssertNoErr(t, err) - - t.Logf("Terminating connection") - err = volumeactions.TerminateConnection(client, cv.ID, connOpts) - th.AssertNoErr(t, err) -} -*/ diff --git a/acceptance/openstack/evs/v3/extensions/volumetenants_test.go b/acceptance/openstack/evs/v3/extensions/volumetenants_test.go index 639588dcb..597d62ca7 100644 --- a/acceptance/openstack/evs/v3/extensions/volumetenants_test.go +++ b/acceptance/openstack/evs/v3/extensions/volumetenants_test.go @@ -3,7 +3,7 @@ package extensions import ( "testing" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/volumetenants" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/volumetenants" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" blockstorage "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/evs/v3" @@ -34,7 +34,7 @@ func TestVolumeTenants(t *testing.T) { volume1, err := blockstorage.CreateVolume(t, client) th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, client, volume1) + t.Cleanup(func() { blockstorage.DeleteVolume(t, client, volume1) }) allPages, err = volumes.List(client, volumes.ListOpts{}).AllPages() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/evs/v3/qos_test.go b/acceptance/openstack/evs/v3/qos_test.go deleted file mode 100644 index 96487383a..000000000 --- a/acceptance/openstack/evs/v3/qos_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package v3 - -import ( - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/qos" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" -) - -func TestQoS(t *testing.T) { - clients.RequireAdmin(t) - - client, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - qos1, err := CreateQoS(t, client) - th.AssertNoErr(t, err) - defer DeleteQoS(t, client, qos1) - - qos2, err := CreateQoS(t, client) - th.AssertNoErr(t, err) - defer DeleteQoS(t, client, qos2) - - getQoS2, err := qos.Get(client, qos2.ID) - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, qos2, getQoS2) - - err = qos.DeleteKeys(client, qos2.ID, qos.DeleteKeysOpts{"read_iops_sec"}) - th.AssertNoErr(t, err) - - updateOpts := qos.UpdateOpts{ - Consumer: qos.ConsumerBack, - Specs: map[string]string{ - "read_iops_sec": "40000", - "write_iops_sec": "40000", - }, - } - - expectedQosSpecs := map[string]string{ - "consumer": "back-end", - "read_iops_sec": "40000", - "write_iops_sec": "40000", - } - - updatedQosSpecs, err := qos.Update(client, qos2.ID, updateOpts) - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, updatedQosSpecs, expectedQosSpecs) - - listOpts := qos.ListOpts{ - Limit: 1, - } - - err = qos.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { - actual, err := qos.ExtractQoS(page) - th.AssertNoErr(t, err) - th.AssertEquals(t, 1, len(actual)) - - var found bool - for _, q := range actual { - if q.ID == qos1.ID || q.ID == qos2.ID { - found = true - } - } - - th.AssertEquals(t, found, true) - - return true, nil - }) - - th.AssertNoErr(t, err) - -} - -func TestQoSAssociations(t *testing.T) { - clients.RequireAdmin(t) - - client, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - qos1, err := CreateQoS(t, client) - th.AssertNoErr(t, err) - defer DeleteQoS(t, client, qos1) - - vt, err := CreateVolumeType(t, client) - th.AssertNoErr(t, err) - defer DeleteVolumeType(t, client, vt) - - associateOpts := qos.AssociateOpts{ - VolumeTypeID: vt.ID, - } - - err = qos.Associate(client, qos1.ID, associateOpts) - th.AssertNoErr(t, err) - - allQosAssociations, err := qos.ListAssociations(client, qos1.ID).AllPages() - th.AssertNoErr(t, err) - - allAssociations, err := qos.ExtractAssociations(allQosAssociations) - th.AssertNoErr(t, err) - tools.PrintResource(t, allAssociations) - th.AssertEquals(t, 1, len(allAssociations)) - th.AssertEquals(t, vt.ID, allAssociations[0].ID) - - disassociateOpts := qos.DisassociateOpts{ - VolumeTypeID: vt.ID, - } - - err = qos.Disassociate(client, qos1.ID, disassociateOpts) - th.AssertNoErr(t, err) - - allQosAssociations, err = qos.ListAssociations(client, qos1.ID).AllPages() - th.AssertNoErr(t, err) - - allAssociations, err = qos.ExtractAssociations(allQosAssociations) - th.AssertNoErr(t, err) - tools.PrintResource(t, allAssociations) - th.AssertEquals(t, 0, len(allAssociations)) - - err = qos.Associate(client, qos1.ID, associateOpts) - th.AssertNoErr(t, err) - - err = qos.DisassociateAll(client, qos1.ID) - th.AssertNoErr(t, err) -} diff --git a/acceptance/openstack/evs/v3/quotaset_test.go b/acceptance/openstack/evs/v3/quotaset_test.go index 7cffc04f5..3161f8f43 100644 --- a/acceptance/openstack/evs/v3/quotaset_test.go +++ b/acceptance/openstack/evs/v3/quotaset_test.go @@ -9,13 +9,10 @@ import ( golangsdk "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumetypes" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" ) func TestQuotasetGet(t *testing.T) { - clients.RequireAdmin(t) - client, projectID := getClientAndProject(t) quotaSet, err := quotasets.Get(client, projectID) @@ -25,8 +22,6 @@ func TestQuotasetGet(t *testing.T) { } func TestQuotasetGetDefaults(t *testing.T) { - clients.RequireAdmin(t) - client, projectID := getClientAndProject(t) quotaSet, err := quotasets.GetDefaults(client, projectID) @@ -36,8 +31,6 @@ func TestQuotasetGetDefaults(t *testing.T) { } func TestQuotasetGetUsage(t *testing.T) { - clients.RequireAdmin(t) - client, projectID := getClientAndProject(t) quotaSetUsage, err := quotasets.GetUsage(client, projectID) @@ -46,111 +39,18 @@ func TestQuotasetGetUsage(t *testing.T) { tools.PrintResource(t, quotaSetUsage) } -var UpdateQuotaOpts = quotasets.UpdateOpts{ - Volumes: golangsdk.IntToPointer(100), - Snapshots: golangsdk.IntToPointer(200), - Gigabytes: golangsdk.IntToPointer(300), - PerVolumeGigabytes: golangsdk.IntToPointer(50), - Backups: golangsdk.IntToPointer(2), - BackupGigabytes: golangsdk.IntToPointer(300), - Groups: golangsdk.IntToPointer(350), - Extra: map[string]interface{}{ - "volumes_foo": golangsdk.IntToPointer(100), - }, -} - -var UpdatedQuotas = quotasets.QuotaSet{ - Volumes: 100, - Snapshots: 200, - Gigabytes: 300, - PerVolumeGigabytes: 50, - Backups: 2, - BackupGigabytes: 300, - Groups: 350, -} - -var VolumeTypeIsPublic = true -var VolumeTypeCreateOpts = volumetypes.CreateOpts{ - Name: "foo", - IsPublic: &VolumeTypeIsPublic, - Description: "foo", - ExtraSpecs: map[string]string{}, -} - -func TestQuotasetUpdate(t *testing.T) { - clients.RequireAdmin(t) - - client, projectID := getClientAndProject(t) - - // save original quotas - orig, err := quotasets.Get(client, projectID) - th.AssertNoErr(t, err) - - // create volumeType to test volume type quota - volumeType, err := volumetypes.Create(client, VolumeTypeCreateOpts) - th.AssertNoErr(t, err) - - defer func() { - restore := quotasets.UpdateOpts{} - FillUpdateOptsFromQuotaSet(*orig, &restore) - - err := volumetypes.Delete(client, volumeType.ID) - th.AssertNoErr(t, err) - - _, err = quotasets.Update(client, projectID, restore) - th.AssertNoErr(t, err) - - }() - - // test Update - resultQuotas, err := quotasets.Update(client, projectID, UpdateQuotaOpts) - th.AssertNoErr(t, err) - - // We dont know the default quotas, so just check if the quotas are not the - // same as before - newQuotas, err := quotasets.Get(client, projectID) - th.AssertNoErr(t, err) - th.AssertEquals(t, resultQuotas.Volumes, newQuotas.Volumes) - th.AssertEquals(t, resultQuotas.Extra["volumes_foo"], newQuotas.Extra["volumes_foo"]) - - // test that resultQuotas.Extra is populated with the 3 new quota types - // for the new volumeType foo, don't take into account other volume types - count := 0 - for k, _ := range resultQuotas.Extra { - tools.PrintResource(t, k) - switch k { - case - "volumes_foo", - "snapshots_foo", - "gigabytes_foo": - count += 1 - } - } - - th.AssertEquals(t, count, 3) - - // unpopulate resultQuotas.Extra as it is different per cloud and test - // rest of the quotaSet - resultQuotas.Extra = map[string]interface{}(nil) - th.AssertDeepEquals(t, UpdatedQuotas, *resultQuotas) -} - func TestQuotasetDelete(t *testing.T) { - clients.RequireAdmin(t) - client, projectID := getClientAndProject(t) // save original quotas - orig, err := quotasets.Get(client, projectID) + _, err := quotasets.Get(client, projectID) th.AssertNoErr(t, err) - defer func() { + t.Cleanup(func() { restore := quotasets.UpdateOpts{} - FillUpdateOptsFromQuotaSet(*orig, &restore) - _, err = quotasets.Update(client, projectID, restore) th.AssertNoErr(t, err) - }() + }) // Obtain environment default quotaset values to validate deletion. defaultQuotaSet, err := quotasets.GetDefaults(client, projectID) @@ -176,13 +76,3 @@ func getClientAndProject(t *testing.T) (*golangsdk.ServiceClient, string) { th.AssertNoErr(t, err) return client, projectID } - -func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) { - dest.Volumes = &src.Volumes - dest.Snapshots = &src.Snapshots - dest.Gigabytes = &src.Gigabytes - dest.PerVolumeGigabytes = &src.PerVolumeGigabytes - dest.Backups = &src.Backups - dest.BackupGigabytes = &src.BackupGigabytes - dest.Groups = &src.Groups -} diff --git a/acceptance/openstack/evs/v3/snapshots_test.go b/acceptance/openstack/evs/v3/snapshots_test.go index f921d813e..96288fac2 100644 --- a/acceptance/openstack/evs/v3/snapshots_test.go +++ b/acceptance/openstack/evs/v3/snapshots_test.go @@ -11,25 +11,23 @@ import ( ) func TestSnapshots(t *testing.T) { - clients.RequireLong(t) - client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volume1, err := CreateVolume(t, client) th.AssertNoErr(t, err) - defer DeleteVolume(t, client, volume1) + t.Cleanup(func() { DeleteVolume(t, client, volume1) }) snapshot1, err := CreateSnapshot(t, client, volume1) th.AssertNoErr(t, err) - defer DeleteSnapshot(t, client, snapshot1) + t.Cleanup(func() { DeleteSnapshot(t, client, snapshot1) }) // Update snapshot updatedSnapshotName := tools.RandomString("ACPTTEST", 16) updatedSnapshotDescription := tools.RandomString("ACPTTEST", 16) updateOpts := snapshots.UpdateOpts{ - Name: &updatedSnapshotName, - Description: &updatedSnapshotDescription, + Name: updatedSnapshotName, + Description: updatedSnapshotDescription, } t.Logf("Attempting to update snapshot: %s", updatedSnapshotName) updatedSnapshot, err := snapshots.Update(client, snapshot1.ID, updateOpts) @@ -41,11 +39,11 @@ func TestSnapshots(t *testing.T) { volume2, err := CreateVolume(t, client) th.AssertNoErr(t, err) - defer DeleteVolume(t, client, volume2) + t.Cleanup(func() { DeleteVolume(t, client, volume2) }) snapshot2, err := CreateSnapshot(t, client, volume2) th.AssertNoErr(t, err) - defer DeleteSnapshot(t, client, snapshot2) + t.Cleanup(func() { DeleteSnapshot(t, client, snapshot2) }) listOpts := snapshots.ListOpts{ Limit: 1, diff --git a/acceptance/openstack/evs/v3/volumeattachments.go b/acceptance/openstack/evs/v3/volumeattachments.go deleted file mode 100644 index 01305e517..000000000 --- a/acceptance/openstack/evs/v3/volumeattachments.go +++ /dev/null @@ -1,110 +0,0 @@ -package v3 - -import ( - "fmt" - "testing" - - golangsdk "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/servers" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/attachments" - v3 "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" -) - -// CreateVolumeAttachment will attach a volume to an instance. An error will be -// returned if the attachment failed. -func CreateVolumeAttachment(t *testing.T, client *golangsdk.ServiceClient, volume *v3.Volume, server *servers.Server) error { - if testing.Short() { - t.Skip("Skipping test that requires volume attachment in short mode.") - } - - attachOpts := &attachments.CreateOpts{ - VolumeUUID: volume.ID, - InstanceUUID: server.ID, - Connector: map[string]interface{}{ - "mode": "rw", - "initiator": "fake", - }, - } - - t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) - - var err error - var attachment *attachments.Attachment - if attachment, err = attachments.Create(client, attachOpts); err != nil { - return err - } - - mv := client.Microversion - client.Microversion = "3.44" - defer func() { - client.Microversion = mv - }() - if err = attachments.Complete(client, attachment.ID); err != nil { - return err - } - - if err = attachments.WaitForStatus(client, attachment.ID, "attached", 60); err != nil { - e := attachments.Delete(client, attachment.ID) - if e != nil { - t.Logf("Failed to delete %q attachment: %s", attachment.ID, err) - } - return err - } - - attachment, err = attachments.Get(client, attachment.ID) - if err != nil { - return err - } - - /* - // Not clear how perform a proper update, OpenStack returns "Unable to update the attachment." - updateOpts := &attachments.UpdateOpts{ - Connector: map[string]interface{}{ - "mode": "ro", - "initiator": "fake", - }, - } - attachment, err = attachments.Update(client, attachment.ID, updateOpts) - if err != nil { - return err - } - */ - - listOpts := &attachments.ListOpts{ - VolumeID: volume.ID, - InstanceID: server.ID, - } - allPages, err := attachments.List(client, listOpts).AllPages() - if err != nil { - return err - } - - allAttachments, err := attachments.ExtractAttachments(allPages) - if err != nil { - return err - } - - if allAttachments[0].ID != attachment.ID { - return fmt.Errorf("Attachment IDs from get and list are not equal: %q != %q", allAttachments[0].ID, attachment.ID) - } - - t.Logf("Attached volume %s to server %s within %q attachment", volume.ID, server.ID, attachment.ID) - - return nil -} - -// DeleteVolumeAttachment will detach a volume from an instance. A fatal error -// will occur if the attachment failed to be deleted. -func DeleteVolumeAttachment(t *testing.T, client *golangsdk.ServiceClient, volume *v3.Volume) { - t.Logf("Attepting to detach volume volume: %s", volume.ID) - - if err := attachments.Delete(client, volume.Attachments[0].AttachmentID); err != nil { - t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) - } - - if err := v3.WaitForStatus(client, volume.ID, "available", 60); err != nil { - t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) - } - - t.Logf("Detached volume: %s", volume.ID) -} diff --git a/acceptance/openstack/evs/v3/volumeattachments_test.go b/acceptance/openstack/evs/v3/volumeattachments_test.go deleted file mode 100644 index f37f7ce2b..000000000 --- a/acceptance/openstack/evs/v3/volumeattachments_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package v3 - -import ( - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" - compute "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/compute/v2" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" -) - -func TestVolumeAttachments(t *testing.T) { - blockClient, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - // minimu required microversion for volume attachments is 3.27 - blockClient.Microversion = "3.27" - - computeClient, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - server, err := compute.CreateServer(t, computeClient) - th.AssertNoErr(t, err) - t.Cleanup(func() { compute.DeleteServer(t, computeClient, server) }) - - volume, err := CreateVolume(t, blockClient) - th.AssertNoErr(t, err) - t.Cleanup(func() { DeleteVolume(t, blockClient, volume) }) - - err = CreateVolumeAttachment(t, blockClient, volume, server) - th.AssertNoErr(t, err) - - newVolume, err := volumes.Get(blockClient, volume.ID) - th.AssertNoErr(t, err) - - DeleteVolumeAttachment(t, blockClient, newVolume) -} diff --git a/openstack/evs/extensions/backups/requests.go b/openstack/evs/extensions/backups/requests.go deleted file mode 100644 index 2ceccc08b..000000000 --- a/openstack/evs/extensions/backups/requests.go +++ /dev/null @@ -1,303 +0,0 @@ -package backups - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToBackupCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Backup. This object is passed to -// the backups.Create function. For more information about these parameters, -// see the Backup object. -type CreateOpts struct { - // VolumeID is the ID of the volume to create the backup from. - VolumeID string `json:"volume_id" required:"true"` - - // Force will force the creation of a backup regardless of the - // volume's status. - Force bool `json:"force,omitempty"` - - // Name is the name of the backup. - Name string `json:"name,omitempty"` - - // Description is the description of the backup. - Description string `json:"description,omitempty"` - - // Metadata is metadata for the backup. - // Requires microversion 3.43 or later. - Metadata map[string]string `json:"metadata,omitempty"` - - // Container is a container to store the backup. - Container string `json:"container,omitempty"` - - // Incremental is whether the backup should be incremental or not. - Incremental bool `json:"incremental,omitempty"` - - // SnapshotID is the ID of a snapshot to backup. - SnapshotID string `json:"snapshot_id,omitempty"` - - // AvailabilityZone is an availability zone to locate the volume or snapshot. - // Requires microversion 3.51 or later. - AvailabilityZone string `json:"availability_zone,omitempty"` -} - -// ToBackupCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToBackupCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "backup") -} - -// Create will create a new Backup based on the values in CreateOpts. To -// extract the Backup object from the response, call the Extract method on the -// CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToBackupCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(createURL(client), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Delete will delete the existing Backup with the provided ID. -func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Get retrieves the Backup with the provided ID. To extract the Backup -// object from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToBackupListQuery() (string, error) -} - -type ListOpts struct { - // AllTenants will retrieve backups of all tenants/projects. - AllTenants bool `q:"all_tenants"` - - // Name will filter by the specified backup name. - // This does not work in later microversions. - Name string `q:"name"` - - // Status will filter by the specified status. - // This does not work in later microversions. - Status string `q:"status"` - - // TenantID will filter by a specific tenant/project ID. - // Setting AllTenants is required to use this. - TenantID string `q:"project_id"` - - // VolumeID will filter by a specified volume ID. - // This does not work in later microversions. - VolumeID string `q:"volume_id"` - - // Comma-separated list of sort keys and optional sort directions in the - // form of [:]. - Sort string `q:"sort"` - - // Requests a page size of items. - Limit int `q:"limit"` - - // Used in conjunction with limit to return a slice of items. - Offset int `q:"offset"` - - // The ID of the last-seen item. - Marker string `q:"marker"` -} - -// ToBackupListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToBackupListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// List returns Backups optionally limited by the conditions provided in -// ListOpts. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToBackupListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return BackupPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// ListDetailOptsBuilder allows extensions to add additional parameters to the ListDetail -// request. -type ListDetailOptsBuilder interface { - ToBackupListDetailQuery() (string, error) -} - -type ListDetailOpts struct { - // AllTenants will retrieve backups of all tenants/projects. - AllTenants bool `q:"all_tenants"` - - // Comma-separated list of sort keys and optional sort directions in the - // form of [:]. - Sort string `q:"sort"` - - // Requests a page size of items. - Limit int `q:"limit"` - - // Used in conjunction with limit to return a slice of items. - Offset int `q:"offset"` - - // The ID of the last-seen item. - Marker string `q:"marker"` - - // True to include `count` in the API response, supported from version 3.45 - WithCount bool `q:"with_count"` -} - -// ToBackupListDetailQuery formats a ListDetailOpts into a query string. -func (opts ListDetailOpts) ToBackupListDetailQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// ListDetail returns more detailed information about Backups optionally -// limited by the conditions provided in ListDetailOpts. -func ListDetail(client *golangsdk.ServiceClient, opts ListDetailOptsBuilder) pagination.Pager { - url := listDetailURL(client) - if opts != nil { - query, err := opts.ToBackupListDetailQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return BackupPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// UpdateOptsBuilder allows extensions to add additional parameters to -// the Update request. -type UpdateOptsBuilder interface { - ToBackupUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contain options for updating an existing Backup. -type UpdateOpts struct { - // Name is the name of the backup. - Name *string `json:"name,omitempty"` - - // Description is the description of the backup. - Description *string `json:"description,omitempty"` - - // Metadata is metadata for the backup. - // Requires microversion 3.43 or later. - Metadata map[string]string `json:"metadata,omitempty"` -} - -// ToBackupUpdateMap assembles a request body based on the contents of -// an UpdateOpts. -func (opts UpdateOpts) ToBackupUpdateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "") -} - -// Update will update the Backup with provided information. To extract -// the updated Backup from the response, call the Extract method on the -// UpdateResult. -// Requires microversion 3.9 or later. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToBackupUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// RestoreOpts contains options for restoring a Backup. This object is passed to -// the backups.RestoreFromBackup function. -type RestoreOpts struct { - // VolumeID is the ID of the existing volume to restore the backup to. - VolumeID string `json:"volume_id,omitempty"` - - // Name is the name of the new volume to restore the backup to. - Name string `json:"name,omitempty"` -} - -// ToRestoreMap assembles a request body based on the contents of a -// RestoreOpts. -func (opts RestoreOpts) ToRestoreMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "restore") -} - -// RestoreFromBackup will restore a Backup to a volume based on the values in -// RestoreOpts. To extract the Restore object from the response, call the -// Extract method on the RestoreResult. -func RestoreFromBackup(client *golangsdk.ServiceClient, id string, opts RestoreOpts) (r RestoreResult) { - b, err := opts.ToRestoreMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(restoreURL(client, id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Export will export a Backup information. To extract the Backup export record -// object from the response, call the Extract method on the ExportResult. -func Export(client *golangsdk.ServiceClient, id string) (r ExportResult) { - resp, err := client.Get(exportURL(client, id), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// ImportOpts contains options for importing a Backup. This object is passed to -// the backups.ImportBackup function. -type ImportOpts BackupRecord - -// ToBackupImportMap assembles a request body based on the contents of a -// ImportOpts. -func (opts ImportOpts) ToBackupImportMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "backup-record") -} - -// Import will import a Backup data to a backup based on the values in -// ImportOpts. To extract the Backup object from the response, call the -// Extract method on the ImportResult. -func Import(client *golangsdk.ServiceClient, opts ImportOpts) (r ImportResult) { - b, err := opts.ToBackupImportMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(importURL(client), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{201}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/extensions/backups/results.go b/openstack/evs/extensions/backups/results.go deleted file mode 100644 index 4cc687287..000000000 --- a/openstack/evs/extensions/backups/results.go +++ /dev/null @@ -1,337 +0,0 @@ -package backups - -import ( - "encoding/json" - "time" - - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// Backup contains all the information associated with a Cinder Backup. -type Backup struct { - // ID is the Unique identifier of the backup. - ID string `json:"id"` - - // CreatedAt is the date the backup was created. - CreatedAt time.Time `json:"-"` - - // UpdatedAt is the date the backup was updated. - UpdatedAt time.Time `json:"-"` - - // Name is the display name of the backup. - Name string `json:"name"` - - // Description is the description of the backup. - Description string `json:"description"` - - // VolumeID is the ID of the Volume from which this backup was created. - VolumeID string `json:"volume_id"` - - // SnapshotID is the ID of the snapshot from which this backup was created. - SnapshotID string `json:"snapshot_id"` - - // Status is the status of the backup. - Status string `json:"status"` - - // Size is the size of the backup, in GB. - Size int `json:"size"` - - // Object Count is the number of objects in the backup. - ObjectCount int `json:"object_count"` - - // Container is the container where the backup is stored. - Container string `json:"container"` - - // HasDependentBackups is whether there are other backups - // depending on this backup. - HasDependentBackups bool `json:"has_dependent_backups"` - - // FailReason has the reason for the backup failure. - FailReason string `json:"fail_reason"` - - // IsIncremental is whether this is an incremental backup. - IsIncremental bool `json:"is_incremental"` - - // DataTimestamp is the time when the data on the volume was first saved. - DataTimestamp time.Time `json:"-"` - - // ProjectID is the ID of the project that owns the backup. This is - // an admin-only field. - ProjectID string `json:"os-backup-project-attr:project_id"` - - // Metadata is metadata about the backup. - // This requires microversion 3.43 or later. - Metadata *map[string]string `json:"metadata"` - - // AvailabilityZone is the Availability Zone of the backup. - // This requires microversion 3.51 or later. - AvailabilityZone *string `json:"availability_zone"` -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - golangsdk.ErrResult -} - -// BackupPage is a pagination.Pager that is returned from a call to the List function. -type BackupPage struct { - pagination.LinkedPageBase -} - -// UnmarshalJSON converts our JSON API response into our backup struct -func (r *Backup) UnmarshalJSON(b []byte) error { - type tmp Backup - var s struct { - tmp - CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` - DataTimestamp golangsdk.JSONRFC3339MilliNoZ `json:"data_timestamp"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Backup(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) - r.DataTimestamp = time.Time(s.DataTimestamp) - - return err -} - -// IsEmpty returns true if a BackupPage contains no Backups. -func (r BackupPage) IsEmpty() (bool, error) { - volumes, err := ExtractBackups(r) - return len(volumes) == 0, err -} - -func (page BackupPage) NextPageURL() (string, error) { - var s struct { - Links []golangsdk.Link `json:"backups_links"` - } - err := page.ExtractInto(&s) - if err != nil { - return "", err - } - return golangsdk.ExtractNextURL(s.Links) -} - -// ExtractBackups extracts and returns Backups. It is used while iterating over a backups.List call. -func ExtractBackups(r pagination.Page) ([]Backup, error) { - var s []Backup - err := ExtractBackupsInto(r, &s) - return s, err -} - -// UpdateResult contains the response body and error from an Update request. -type UpdateResult struct { - commonResult -} - -type commonResult struct { - golangsdk.Result -} - -// Extract will get the Backup object out of the commonResult object. -func (r commonResult) Extract() (*Backup, error) { - var s Backup - err := r.ExtractInto(&s) - return &s, err -} - -func (r commonResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "backup") -} - -func ExtractBackupsInto(r pagination.Page, v interface{}) error { - return r.(BackupPage).Result.ExtractIntoSlicePtr(v, "backups") -} - -// RestoreResult contains the response body and error from a restore request. -type RestoreResult struct { - commonResult -} - -// Restore contains all the information associated with a Cinder Backup restore -// response. -type Restore struct { - // BackupID is the Unique identifier of the backup. - BackupID string `json:"backup_id"` - - // VolumeID is the Unique identifier of the volume. - VolumeID string `json:"volume_id"` - - // Name is the name of the volume, where the backup was restored to. - VolumeName string `json:"volume_name"` -} - -// Extract will get the Backup restore object out of the RestoreResult object. -func (r RestoreResult) Extract() (*Restore, error) { - var s Restore - err := r.ExtractInto(&s) - return &s, err -} - -func (r RestoreResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "restore") -} - -// ExportResult contains the response body and error from an export request. -type ExportResult struct { - commonResult -} - -// BackupRecord contains an information about a backup backend storage. -type BackupRecord struct { - // The service used to perform the backup. - BackupService string `json:"backup_service"` - - // An identifier string to locate the backup. - BackupURL []byte `json:"backup_url"` -} - -// Extract will get the Backup record object out of the ExportResult object. -func (r ExportResult) Extract() (*BackupRecord, error) { - var s BackupRecord - err := r.ExtractInto(&s) - return &s, err -} - -func (r ExportResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "backup-record") -} - -// ImportResponse struct contains the response of the Backup Import action. -type ImportResponse struct { - ID string `json:"id"` - Name string `json:"name"` -} - -// ImportResult contains the response body and error from an import request. -type ImportResult struct { - golangsdk.Result -} - -// Extract will get the Backup object out of the commonResult object. -func (r ImportResult) Extract() (*ImportResponse, error) { - var s ImportResponse - err := r.ExtractInto(&s) - return &s, err -} - -func (r ImportResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "backup") -} - -// ImportBackup contains all the information to import a Cinder Backup. -type ImportBackup struct { - ID string `json:"id"` - CreatedAt time.Time `json:"-"` - UpdatedAt time.Time `json:"-"` - VolumeID string `json:"volume_id"` - SnapshotID *string `json:"snapshot_id"` - Status *string `json:"status"` - Size *int `json:"size"` - ObjectCount *int `json:"object_count"` - Container *string `json:"container"` - ServiceMetadata *string `json:"service_metadata"` - Service *string `json:"service"` - Host *string `json:"host"` - UserID string `json:"user_id"` - DeletedAt time.Time `json:"-"` - DataTimestamp time.Time `json:"-"` - TempSnapshotID *string `json:"temp_snapshot_id"` - TempVolumeID *string `json:"temp_volume_id"` - RestoreVolumeID *string `json:"restore_volume_id"` - NumDependentBackups *int `json:"num_dependent_backups"` - EncryptionKeyID *string `json:"encryption_key_id"` - ParentID *string `json:"parent_id"` - Deleted bool `json:"deleted"` - DisplayName *string `json:"display_name"` - DisplayDescription *string `json:"display_description"` - DriverInfo interface{} `json:"driver_info"` - FailReason *string `json:"fail_reason"` - ProjectID string `json:"project_id"` - Metadata map[string]string `json:"metadata"` - AvailabilityZone *string `json:"availability_zone"` -} - -// UnmarshalJSON converts our JSON API response into our backup struct -func (r *ImportBackup) UnmarshalJSON(b []byte) error { - type tmp ImportBackup - var s struct { - tmp - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - DeletedAt time.Time `json:"deleted_at"` - DataTimestamp time.Time `json:"data_timestamp"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = ImportBackup(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) - r.DeletedAt = time.Time(s.DeletedAt) - r.DataTimestamp = time.Time(s.DataTimestamp) - - return err -} - -// MarshalJSON converts our struct request into JSON backup import request -func (r ImportBackup) MarshalJSON() ([]byte, error) { - type b ImportBackup - type ext struct { - CreatedAt *string `json:"created_at"` - UpdatedAt *string `json:"updated_at"` - DeletedAt *string `json:"deleted_at"` - DataTimestamp *string `json:"data_timestamp"` - } - type tmp struct { - b - ext - } - - var t ext - if r.CreatedAt != (time.Time{}) { - v := r.CreatedAt.Format(time.RFC3339) - t.CreatedAt = &v - } - if r.UpdatedAt != (time.Time{}) { - v := r.UpdatedAt.Format(time.RFC3339) - t.UpdatedAt = &v - } - if r.DeletedAt != (time.Time{}) { - v := r.DeletedAt.Format(time.RFC3339) - t.DeletedAt = &v - } - if r.DataTimestamp != (time.Time{}) { - v := r.DataTimestamp.Format(time.RFC3339) - t.DataTimestamp = &v - } - - if r.Metadata == nil { - r.Metadata = make(map[string]string) - } - - s := tmp{ - b(r), - t, - } - - return json.Marshal(s) -} diff --git a/openstack/evs/extensions/backups/urls.go b/openstack/evs/extensions/backups/urls.go deleted file mode 100644 index a23b8a471..000000000 --- a/openstack/evs/extensions/backups/urls.go +++ /dev/null @@ -1,39 +0,0 @@ -package backups - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func createURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("backups") -} - -func deleteURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("backups", id) -} - -func getURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("backups", id) -} - -func listURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("backups") -} - -func listDetailURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("backups", "detail") -} - -func updateURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("backups", id) -} - -func restoreURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("backups", id, "restore") -} - -func exportURL(c *golangsdk.ServiceClient, id string) string { - return c.ServiceURL("backups", id, "export_record") -} - -func importURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("backups", "import_record") -} diff --git a/openstack/evs/extensions/limits/requests.go b/openstack/evs/extensions/limits/requests.go deleted file mode 100644 index 7d621fdd3..000000000 --- a/openstack/evs/extensions/limits/requests.go +++ /dev/null @@ -1,13 +0,0 @@ -package limits - -import ( - golangsdk "github.com/opentelekomcloud/gophertelekomcloud" -) - -// Get returns the limits about the currently scoped tenant. -func Get(client *golangsdk.ServiceClient) (r GetResult) { - url := getURL(client) - resp, err := client.Get(url, &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/extensions/limits/results.go b/openstack/evs/extensions/limits/results.go deleted file mode 100644 index 2667a0713..000000000 --- a/openstack/evs/extensions/limits/results.go +++ /dev/null @@ -1,80 +0,0 @@ -package limits - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// Limits is a struct that contains the response of a limit query. -type Limits struct { - // Absolute contains the limits and usage information. - // An absolute limit value of -1 indicates that the absolute limit for the item is infinite. - Absolute Absolute `json:"absolute"` - // Rate contains rate-limit volume copy bandwidth, used to mitigate slow down of data access from the instances. - Rate []Rate `json:"rate"` -} - -// Absolute is a struct that contains the current resource usage and limits -// of a project. -type Absolute struct { - // MaxTotalVolumes is the maximum number of volumes. - MaxTotalVolumes int `json:"maxTotalVolumes"` - - // MaxTotalSnapshots is the maximum number of snapshots. - MaxTotalSnapshots int `json:"maxTotalSnapshots"` - - // MaxTotalVolumeGigabytes is the maximum total amount of volumes, in gibibytes (GiB). - MaxTotalVolumeGigabytes int `json:"maxTotalVolumeGigabytes"` - - // MaxTotalBackups is the maximum number of backups. - MaxTotalBackups int `json:"maxTotalBackups"` - - // MaxTotalBackupGigabytes is the maximum total amount of backups, in gibibytes (GiB). - MaxTotalBackupGigabytes int `json:"maxTotalBackupGigabytes"` - - // TotalVolumesUsed is the total number of volumes used. - TotalVolumesUsed int `json:"totalVolumesUsed"` - - // TotalGigabytesUsed is the total number of gibibytes (GiB) used. - TotalGigabytesUsed int `json:"totalGigabytesUsed"` - - // TotalSnapshotsUsed the total number of snapshots used. - TotalSnapshotsUsed int `json:"totalSnapshotsUsed"` - - // TotalBackupsUsed is the total number of backups used. - TotalBackupsUsed int `json:"totalBackupsUsed"` - - // TotalBackupGigabytesUsed is the total number of backups gibibytes (GiB) used. - TotalBackupGigabytesUsed int `json:"totalBackupGigabytesUsed"` -} - -// Rate is a struct that contains the -// rate-limit volume copy bandwidth, used to mitigate slow down of data access from the instances. -type Rate struct { - Regex string `json:"regex"` - URI string `json:"uri"` - Limit []Limit `json:"limit"` -} - -// Limit struct contains Limit values for the Rate struct -type Limit struct { - Verb string `json:"verb"` - NextAvailable string `json:"next-available"` - Unit string `json:"unit"` - Value int `json:"value"` - Remaining int `json:"remaining"` -} - -// Extract interprets a limits result as a Limits. -func (r GetResult) Extract() (*Limits, error) { - var s struct { - Limits *Limits `json:"limits"` - } - err := r.ExtractInto(&s) - return s.Limits, err -} - -// GetResult is the response from a Get operation. Call its Extract -// method to interpret it as an Absolute. -type GetResult struct { - golangsdk.Result -} diff --git a/openstack/evs/extensions/limits/urls.go b/openstack/evs/extensions/limits/urls.go deleted file mode 100644 index 268f791d1..000000000 --- a/openstack/evs/extensions/limits/urls.go +++ /dev/null @@ -1,11 +0,0 @@ -package limits - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -const resourcePath = "limits" - -func getURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL(resourcePath) -} diff --git a/openstack/evs/extensions/volumetenants.go b/openstack/evs/extensions/volumetenants.go deleted file mode 100644 index 50fdb32b4..000000000 --- a/openstack/evs/extensions/volumetenants.go +++ /dev/null @@ -1,6 +0,0 @@ -package extensions - -type VolumeTenantExt struct { - // TenantID is the id of the project that owns the volume. - TenantID string `json:"os-vol-tenant-attr:tenant_id"` -} diff --git a/openstack/evs/v2/volumes/testing/requests_test.go b/openstack/evs/v2/volumes/testing/requests_test.go index 4c18800e6..c8db5adf7 100644 --- a/openstack/evs/v2/volumes/testing/requests_test.go +++ b/openstack/evs/v2/volumes/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/volumetenants" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v2/volumes" "github.com/opentelekomcloud/gophertelekomcloud/pagination" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" @@ -102,7 +102,7 @@ func TestListAllWithExtensions(t *testing.T) { type VolumeWithExt struct { volumes.Volume - extensions.VolumeTenantExt + volumetenants.VolumeTenantExt } allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() @@ -245,7 +245,7 @@ func TestGetWithExtensions(t *testing.T) { var s struct { volumes.Volume - extensions.VolumeTenantExt + volumetenants.VolumeTenantExt } err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) diff --git a/openstack/evs/v3/attachments/requests.go b/openstack/evs/v3/attachments/requests.go deleted file mode 100644 index 1edb58bcf..000000000 --- a/openstack/evs/v3/attachments/requests.go +++ /dev/null @@ -1,180 +0,0 @@ -package attachments - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToAttachmentCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Volume attachment. This object is -// passed to the Create function. For more information about these parameters, -// see the Attachment object. -type CreateOpts struct { - // VolumeUUID is the UUID of the Cinder volume to create the attachment - // record for. - VolumeUUID string `json:"volume_uuid"` - // InstanceUUID is the ID of the Server to create the attachment for. - // When attaching to a Nova Server this is the Nova Server (Instance) - // UUID. - InstanceUUID string `json:"instance_uuid"` - // Connector is an optional map containing all of the needed atachment - // information for exmaple initiator IQN, etc. - Connector map[string]interface{} `json:"connector,omitempty"` - // Mode is an attachment mode. Acceptable values are read-only ('ro') - // and read-and-write ('rw'). Available only since 3.54 microversion. - // For APIs from 3.27 till 3.53 use Connector["mode"] = "rw|ro". - Mode string `json:"mode,omitempty"` -} - -// ToAttachmentCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToAttachmentCreateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "attachment") -} - -// Create will create a new Attachment based on the values in CreateOpts. To -// extract the Attachment object from the response, call the Extract method on -// the CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToAttachmentCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("attachments"), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200, 202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Delete will delete the existing Attachment with the provided ID. -func Delete(client *golangsdk.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(client.ServiceURL("attachments", id), &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Get retrieves the Attachment with the provided ID. To extract the Attachment -// object from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(client.ServiceURL("attachments", id), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToAttachmentListQuery() (string, error) -} - -// ListOpts holds options for listing Attachments. It is passed to the attachments.List -// function. -type ListOpts struct { - // AllTenants will retrieve attachments of all tenants/projects. - AllTenants bool `q:"all_tenants"` - - // Status will filter by the specified status. - Status string `q:"status"` - - // ProjectID will filter by a specific tenant/project ID. - ProjectID string `q:"project_id"` - - // VolumeID will filter by a specific volume ID. - VolumeID string `q:"volume_id"` - - // InstanceID will filter by a specific instance ID. - InstanceID string `q:"instance_id"` - - // Comma-separated list of sort keys and optional sort directions in the - // form of [:]. - Sort string `q:"sort"` - - // Requests a page size of items. - Limit int `q:"limit"` - - // Used in conjunction with limit to return a slice of items. - Offset int `q:"offset"` - - // The ID of the last-seen item. - Marker string `q:"marker"` -} - -// ToAttachmentListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToAttachmentListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// List returns Attachments optionally limited by the conditions provided in -// ListOpts. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := client.ServiceURL("attachments", "detail") - if opts != nil { - query, err := opts.ToAttachmentListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return AttachmentPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToAttachmentUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contain options for updating an existing Attachment. -// This is used to finalize an attachment that was created without a -// connector (reserve). -type UpdateOpts struct { - Connector map[string]interface{} `json:"connector"` -} - -// ToAttachmentUpdateMap assembles a request body based on the contents of an -// UpdateOpts. -func (opts UpdateOpts) ToAttachmentUpdateMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "attachment") -} - -// Update will update the Attachment with provided information. To extract the -// updated Attachment from the response, call the Extract method on the -// UpdateResult. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToAttachmentUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(client.ServiceURL("attachments", id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// Complete will complete an attachment for a cinder volume. -// Available starting in the 3.44 microversion. -func Complete(client *golangsdk.ServiceClient, id string) (r CompleteResult) { - b := map[string]interface{}{ - "os-complete": nil, - } - resp, err := client.Post(client.ServiceURL("attachments", id, "action"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{204}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/attachments/results.go b/openstack/evs/v3/attachments/results.go deleted file mode 100644 index 45618a3cb..000000000 --- a/openstack/evs/v3/attachments/results.go +++ /dev/null @@ -1,120 +0,0 @@ -// Package attachments provides access to OpenStack Block Storage Attachment -// API's. Use of this package requires Cinder version 3.27 at a minimum. -package attachments - -import ( - "encoding/json" - "time" - - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// Attachment contains all the information associated with an OpenStack -// Attachment. -type Attachment struct { - // ID is the Unique identifier for the attachment. - ID string `json:"id"` - // VolumeID is the UUID of the Volume associated with this attachment. - VolumeID string `json:"volume_id"` - // Instance is the Instance/Server UUID associated with this attachment. - Instance string `json:"instance"` - // AttachedAt is the time the attachment was created. - AttachedAt time.Time `json:"-"` - // DetachedAt is the time the attachment was detached. - DetachedAt time.Time `json:"-"` - // Status is the current attach status. - Status string `json:"status"` - // AttachMode includes things like Read Only etc. - AttachMode string `json:"attach_mode"` - // ConnectionInfo is the required info for a node to make a connection - // provided by the driver. - ConnectionInfo map[string]interface{} `json:"connection_info"` -} - -// UnmarshalJSON is our unmarshalling helper -func (r *Attachment) UnmarshalJSON(b []byte) error { - type tmp Attachment - var s struct { - tmp - AttachedAt golangsdk.JSONRFC3339MilliNoZ `json:"attached_at"` - DetachedAt golangsdk.JSONRFC3339MilliNoZ `json:"detached_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Attachment(s.tmp) - - r.AttachedAt = time.Time(s.AttachedAt) - r.DetachedAt = time.Time(s.DetachedAt) - - return err -} - -// AttachmentPage is a pagination.pager that is returned from a call to the List -// function. -type AttachmentPage struct { - pagination.LinkedPageBase -} - -// IsEmpty returns true if a ListResult contains no Attachments. -func (r AttachmentPage) IsEmpty() (bool, error) { - attachments, err := ExtractAttachments(r) - return len(attachments) == 0, err -} - -// ExtractAttachments extracts and returns Attachments. It is used while -// iterating over a attachment.List call. -func ExtractAttachments(r pagination.Page) ([]Attachment, error) { - var s []Attachment - err := ExtractAttachmentsInto(r, &s) - return s, err -} - -type commonResult struct { - golangsdk.Result -} - -// Extract will get the Attachment object out of the commonResult object. -func (r commonResult) Extract() (*Attachment, error) { - var s Attachment - err := r.ExtractInto(&s) - return &s, err -} - -// ExtractInto converts our response data into a attachment struct. -func (r commonResult) ExtractInto(a interface{}) error { - return r.Result.ExtractIntoStructPtr(a, "attachment") -} - -// ExtractAttachmentsInto similar to ExtractInto but operates on a List of -// attachments. -func ExtractAttachmentsInto(r pagination.Page, a interface{}) error { - return r.(AttachmentPage).Result.ExtractIntoSlicePtr(a, "attachments") -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// UpdateResult contains the response body and error from an Update request. -type UpdateResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - golangsdk.ErrResult -} - -// CompleteResult contains the response body and error from a Complete request. -type CompleteResult struct { - golangsdk.ErrResult -} diff --git a/openstack/evs/v3/attachments/util.go b/openstack/evs/v3/attachments/util.go deleted file mode 100644 index 4f7b9dfab..000000000 --- a/openstack/evs/v3/attachments/util.go +++ /dev/null @@ -1,22 +0,0 @@ -package attachments - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { - return golangsdk.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} diff --git a/openstack/evs/v3/qos/requests.go b/openstack/evs/v3/qos/requests.go deleted file mode 100644 index fd5635d0c..000000000 --- a/openstack/evs/v3/qos/requests.go +++ /dev/null @@ -1,328 +0,0 @@ -package qos - -import ( - golangsdk "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -type CreateOptsBuilder interface { - ToQoSCreateMap() (map[string]interface{}, error) -} - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToQoSListQuery() (string, error) -} - -type QoSConsumer string - -const ( - ConsumerFront QoSConsumer = "front-end" - ConsumerBack QoSConsumer = "back-end" - ConsumerBoth QoSConsumer = "both" -) - -// CreateOpts contains options for creating a QoS specification. -// This object is passed to the qos.Create function. -type CreateOpts struct { - // The name of the QoS spec - Name string `json:"name"` - // The consumer of the QoS spec. Possible values are - // both, front-end, back-end. - Consumer QoSConsumer `json:"consumer,omitempty"` - // Specs is a collection of miscellaneous key/values used to set - // specifications for the QoS - Specs map[string]string `json:"-"` -} - -// ToQoSCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToQoSCreateMap() (map[string]interface{}, error) { - b, err := golangsdk.BuildRequestBody(opts, "qos_specs") - if err != nil { - return nil, err - } - - if opts.Specs != nil { - if v, ok := b["qos_specs"].(map[string]interface{}); ok { - for key, value := range opts.Specs { - v[key] = value - } - } - } - - return b, nil -} - -// Create will create a new QoS based on the values in CreateOpts. To extract -// the QoS object from the response, call the Extract method on the -// CreateResult. -func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToQoSCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(client.ServiceURL("qos-specs"), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// DeleteOptsBuilder allows extensions to add additional parameters to the -// Delete request. -type DeleteOptsBuilder interface { - ToQoSDeleteQuery() (string, error) -} - -// DeleteOpts contains options for deleting a QoS. This object is passed to -// the qos.Delete function. -type DeleteOpts struct { - // Delete a QoS specification even if it is in-use - Force bool `q:"force"` -} - -// ToQoSDeleteQuery formats a DeleteOpts into a query string. -func (opts DeleteOpts) ToQoSDeleteQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// Delete will delete the existing QoS with the provided ID. -func Delete(client *golangsdk.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { - url := client.ServiceURL("qos-specs", id) - if opts != nil { - query, err := opts.ToQoSDeleteQuery() - if err != nil { - r.Err = err - return - } - url += query - } - resp, err := client.Delete(url, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -type ListOpts struct { - // Sort is Comma-separated list of sort keys and optional sort - // directions in the form of < key > [: < direction > ]. A valid - // direction is asc (ascending) or desc (descending). - Sort string `q:"sort"` - - // Marker and Limit control paging. - // Marker instructs List where to start listing from. - Marker string `q:"marker"` - - // Limit instructs List to refrain from sending excessively large lists of - // QoS. - Limit int `q:"limit"` -} - -// ToQoSListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToQoSListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// List instructs OpenStack to provide a list of QoS. -// You may provide criteria by which List curtails its results for easier -// processing. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := client.ServiceURL("qos-specs") - if opts != nil { - query, err := opts.ToQoSListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return QoSPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// Get retrieves details of a single qos. Use Extract to convert its -// result into a QoS. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(client.ServiceURL("qos-specs", id), &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// CreateQosSpecsOptsBuilder allows extensions to add additional parameters to the -// CreateQosSpecs requests. -type CreateQosSpecsOptsBuilder interface { - ToQosSpecsCreateMap() (map[string]interface{}, error) -} - -// UpdateOpts contains options for creating a QoS specification. -// This object is passed to the qos.Update function. -type UpdateOpts struct { - // The consumer of the QoS spec. Possible values are - // both, front-end, back-end. - Consumer QoSConsumer `json:"consumer,omitempty"` - // Specs is a collection of miscellaneous key/values used to set - // specifications for the QoS - Specs map[string]string `json:"-"` -} - -type UpdateOptsBuilder interface { - ToQoSUpdateMap() (map[string]interface{}, error) -} - -// ToQoSUpdateMap assembles a request body based on the contents of a -// UpdateOpts. -func (opts UpdateOpts) ToQoSUpdateMap() (map[string]interface{}, error) { - b, err := golangsdk.BuildRequestBody(opts, "qos_specs") - if err != nil { - return nil, err - } - - if opts.Specs != nil { - if v, ok := b["qos_specs"].(map[string]interface{}); ok { - for key, value := range opts.Specs { - v[key] = value - } - } - } - - return b, nil -} - -// Update will update an existing QoS based on the values in UpdateOpts. -// To extract the QoS object from the response, call the Extract method -// on the UpdateResult. -func Update(client *golangsdk.ServiceClient, id string, opts UpdateOptsBuilder) (r updateResult) { - b, err := opts.ToQoSUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(client.ServiceURL("qos-specs", id), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// DeleteKeysOptsBuilder allows extensions to add additional parameters to the -// CreateExtraSpecs requests. -type DeleteKeysOptsBuilder interface { - ToDeleteKeysCreateMap() (map[string]interface{}, error) -} - -// DeleteKeysOpts is a string slice that contains keys to be deleted. -type DeleteKeysOpts []string - -// ToDeleteKeysCreateMap assembles a body for a Create request based on -// the contents of ExtraSpecsOpts. -func (opts DeleteKeysOpts) ToDeleteKeysCreateMap() (map[string]interface{}, error) { - return map[string]interface{}{"keys": opts}, nil -} - -// DeleteKeys will delete the keys/specs from the specified QoS -func DeleteKeys(client *golangsdk.ServiceClient, qosID string, opts DeleteKeysOptsBuilder) (r DeleteResult) { - b, err := opts.ToDeleteKeysCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(client.ServiceURL("qos-specs", qosID, "delete_keys"), b, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// AssociateOpitsBuilder allows extensions to define volume type id -// to the associate query -type AssociateOptsBuilder interface { - ToQosAssociateQuery() (string, error) -} - -// AssociateOpts contains options for associating a QoS with a -// volume type -type AssociateOpts struct { - VolumeTypeID string `q:"vol_type_id" required:"true"` -} - -// ToQosAssociateQuery formats an AssociateOpts into a query string -func (opts AssociateOpts) ToQosAssociateQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// Associate will associate a qos with a volute type -func Associate(client *golangsdk.ServiceClient, qosID string, opts AssociateOptsBuilder) (r AssociateResult) { - url := client.ServiceURL("qos-specs", qosID, "associate") - query, err := opts.ToQosAssociateQuery() - if err != nil { - r.Err = err - return - } - url += query - - resp, err := client.Get(url, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// DisassociateOpitsBuilder allows extensions to define volume type id -// to the disassociate query -type DisassociateOptsBuilder interface { - ToQosDisassociateQuery() (string, error) -} - -// DisassociateOpts contains options for disassociating a QoS from a -// volume type -type DisassociateOpts struct { - VolumeTypeID string `q:"vol_type_id" required:"true"` -} - -// ToQosDisassociateQuery formats a DisassociateOpts into a query string -func (opts DisassociateOpts) ToQosDisassociateQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// Disassociate will disassociate a qos from a volute type -func Disassociate(client *golangsdk.ServiceClient, qosID string, opts DisassociateOptsBuilder) (r DisassociateResult) { - url := client.ServiceURL("qos-specs", qosID, "disassociate") - query, err := opts.ToQosDisassociateQuery() - if err != nil { - r.Err = err - return - } - url += query - - resp, err := client.Get(url, nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// DisassociateAll will disassociate a qos from all volute types -func DisassociateAll(client *golangsdk.ServiceClient, qosID string) (r DisassociateAllResult) { - resp, err := client.Get(client.ServiceURL("qos-specs", qosID, "disassociate_all"), nil, &golangsdk.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// ListAssociations retrieves the associations of a QoS. -func ListAssociations(client *golangsdk.ServiceClient, qosID string) pagination.Pager { - url := client.ServiceURL("qos-specs", qosID, "associations") - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return AssociationPage{pagination.SinglePageBase(r)} - }) -} diff --git a/openstack/evs/v3/qos/results.go b/openstack/evs/v3/qos/results.go deleted file mode 100644 index 93abf3b97..000000000 --- a/openstack/evs/v3/qos/results.go +++ /dev/null @@ -1,144 +0,0 @@ -package qos - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// QoS contains all the information associated with an OpenStack QoS specification. -type QoS struct { - // Name is the name of the QoS. - Name string `json:"name"` - // Unique identifier for the QoS. - ID string `json:"id"` - // Consumer of QoS - Consumer string `json:"consumer"` - // Arbitrary key-value pairs defined by the user. - Specs map[string]string `json:"specs"` -} - -type commonResult struct { - golangsdk.Result -} - -// Extract will get the QoS object out of the commonResult object. -func (r commonResult) Extract() (*QoS, error) { - var s QoS - err := r.ExtractInto(&s) - return &s, err -} - -// ExtractInto converts our response data into a QoS struct -func (r commonResult) ExtractInto(qos interface{}) error { - return r.Result.ExtractIntoStructPtr(qos, "qos_specs") -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - golangsdk.ErrResult -} - -type QoSPage struct { - pagination.LinkedPageBase -} - -// IsEmpty determines if a QoSPage contains any results. -func (page QoSPage) IsEmpty() (bool, error) { - qos, err := ExtractQoS(page) - return len(qos) == 0, err -} - -// NextPageURL uses the response's embedded link reference to navigate to the -// next page of results. -func (page QoSPage) NextPageURL() (string, error) { - var s struct { - Links []golangsdk.Link `json:"qos_specs_links"` - } - err := page.ExtractInto(&s) - if err != nil { - return "", err - } - return golangsdk.ExtractNextURL(s.Links) -} - -// ExtractQoS provides access to the list of qos in a page acquired -// from the List operation. -func ExtractQoS(r pagination.Page) ([]QoS, error) { - var s struct { - QoSs []QoS `json:"qos_specs"` - } - err := (r.(QoSPage)).ExtractInto(&s) - return s.QoSs, err -} - -// GetResult is the response of a Get operations. Call its Extract method to -// interpret it as a Flavor. -type GetResult struct { - commonResult -} - -// Extract interprets any updateResult as qosSpecs, if possible. -func (r updateResult) Extract() (map[string]string, error) { - var s struct { - QosSpecs map[string]string `json:"qos_specs"` - } - err := r.ExtractInto(&s) - return s.QosSpecs, err -} - -// updateResult contains the result of a call for (potentially) multiple -// key-value pairs. Call its Extract method to interpret it as a -// map[string]interface. -type updateResult struct { - golangsdk.Result -} - -// AssociateResult contains the response body and error from a Associate request. -type AssociateResult struct { - golangsdk.ErrResult -} - -// DisassociateResult contains the response body and error from a Disassociate request. -type DisassociateResult struct { - golangsdk.ErrResult -} - -// DisassociateAllResult contains the response body and error from a DisassociateAll request. -type DisassociateAllResult struct { - golangsdk.ErrResult -} - -// QoS contains all the information associated with an OpenStack QoS specification. -type QosAssociation struct { - // Name is the name of the associated resource - Name string `json:"name"` - // Unique identifier of the associated resources - ID string `json:"id"` - // AssociationType of the QoS Association - AssociationType string `json:"association_type"` -} - -// AssociationPage contains a single page of all Associations of a QoS -type AssociationPage struct { - pagination.SinglePageBase -} - -// IsEmpty indicates whether an Association page is empty. -func (page AssociationPage) IsEmpty() (bool, error) { - v, err := ExtractAssociations(page) - return len(v) == 0, err -} - -// ExtractAssociations interprets a page of results as a slice of QosAssociations -func ExtractAssociations(r pagination.Page) ([]QosAssociation, error) { - var s struct { - QosAssociations []QosAssociation `json:"qos_associations"` - } - err := (r.(AssociationPage)).ExtractInto(&s) - return s.QosAssociations, err -} diff --git a/openstack/evs/v3/snapshots/Create.go b/openstack/evs/v3/snapshots/Create.go index bb4fc2f30..bb232b4c3 100644 --- a/openstack/evs/v3/snapshots/Create.go +++ b/openstack/evs/v3/snapshots/Create.go @@ -29,8 +29,7 @@ type CreateOpts struct { } // Create will create a new Snapshot based on the values in CreateOpts. To -// extract the Snapshot object from the response, call the Extract method on the -// CreateResult. +// extract the Snapshot object from the response, call the Extract method on the CreateResult. func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Snapshot, error) { b, err := build.RequestBody(opts, "snapshot") if err != nil { diff --git a/openstack/evs/v3/snapshots/Get.go b/openstack/evs/v3/snapshots/Get.go new file mode 100644 index 000000000..43de32aa7 --- /dev/null +++ b/openstack/evs/v3/snapshots/Get.go @@ -0,0 +1,11 @@ +package snapshots + +import "github.com/opentelekomcloud/gophertelekomcloud" + +// Get retrieves the Snapshot with the provided ID. To extract the Snapshot +// object from the response, call the Extract method on the GetResult. +func Get(client *golangsdk.ServiceClient, id string) (*Snapshot, error) { + // GET /v3/{project_id}/snapshots/{snapshot_id} + raw, err := client.Get(client.ServiceURL("snapshots", id), nil, nil) + return extra(err, raw) +} diff --git a/openstack/evs/v3/snapshots/requests.go b/openstack/evs/v3/snapshots/requests.go deleted file mode 100644 index 4ed4b1c5b..000000000 --- a/openstack/evs/v3/snapshots/requests.go +++ /dev/null @@ -1,48 +0,0 @@ -package snapshots - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// Get retrieves the Snapshot with the provided ID. To extract the Snapshot -// object from the response, call the Extract method on the GetResult. -func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(client.ServiceURL("snapshots", id), &r.Body, nil) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} - -// UpdateMetadataOptsBuilder allows extensions to add additional parameters to -// the Update request. -type UpdateMetadataOptsBuilder interface { - ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) -} - -// UpdateMetadataOpts contain options for updating an existing Snapshot. This -// object is passed to the snapshots.Update function. For more information -// about the parameters, see the Snapshot object. -type UpdateMetadataOpts struct { - Metadata map[string]interface{} `json:"metadata,omitempty"` -} - -// ToSnapshotUpdateMetadataMap assembles a request body based on the contents of -// an UpdateMetadataOpts. -func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { - return golangsdk.BuildRequestBody(opts, "") -} - -// UpdateMetadata will update the Snapshot with provided information. To -// extract the updated Snapshot from the response, call the ExtractMetadata -// method on the UpdateMetadataResult. -func UpdateMetadata(client *golangsdk.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { - b, err := opts.ToSnapshotUpdateMetadataMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(client.ServiceURL("snapshots", id, "metadata"), b, &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = golangsdk.ParseResponse(resp, err) - return -} diff --git a/openstack/evs/v3/snapshots/results.go b/openstack/evs/v3/snapshots/results.go index ce15ab8c0..f1ab27f6d 100644 --- a/openstack/evs/v3/snapshots/results.go +++ b/openstack/evs/v3/snapshots/results.go @@ -31,26 +31,6 @@ type Snapshot struct { Metadata map[string]string `json:"metadata"` } -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - golangsdk.ErrResult -} - -// UpdateResult contains the response body and error from an Update request. -type UpdateResult struct { - commonResult -} - // UnmarshalJSON converts our JSON API response into our snapshot struct func (r *Snapshot) UnmarshalJSON(b []byte) error { type tmp Snapshot @@ -71,24 +51,6 @@ func (r *Snapshot) UnmarshalJSON(b []byte) error { return err } -// UpdateMetadataResult contains the response body and error from an UpdateMetadata request. -type UpdateMetadataResult struct { - commonResult -} - -// ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. -func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { - if r.Err != nil { - return nil, r.Err - } - m := r.Body.(map[string]interface{})["metadata"] - return m.(map[string]interface{}), nil -} - -type commonResult struct { - golangsdk.Result -} - func extra(err error, raw *http.Response) (*Snapshot, error) { if err != nil { return nil, err @@ -98,3 +60,20 @@ func extra(err error, raw *http.Response) (*Snapshot, error) { err = extract.IntoStructPtr(raw.Body, &res, "snapshot") return &res, err } + +// WaitForStatus will continually poll the resource, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { + return golangsdk.WaitFor(secs, func() (bool, error) { + current, err := Get(c, id) + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} diff --git a/openstack/evs/v3/snapshots/util.go b/openstack/evs/v3/snapshots/util.go deleted file mode 100644 index 2375926d2..000000000 --- a/openstack/evs/v3/snapshots/util.go +++ /dev/null @@ -1,22 +0,0 @@ -package snapshots - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { - return golangsdk.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} From ced95f3d1adf68ff3c82f97b4ceb3aa6f22c520f Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Tue, 8 Nov 2022 15:46:45 +0100 Subject: [PATCH 45/51] test --- .../evs/v3/extensions/limits_test.go | 37 ------------------- 1 file changed, 37 deletions(-) delete mode 100644 acceptance/openstack/evs/v3/extensions/limits_test.go diff --git a/acceptance/openstack/evs/v3/extensions/limits_test.go b/acceptance/openstack/evs/v3/extensions/limits_test.go deleted file mode 100644 index a0b837625..000000000 --- a/acceptance/openstack/evs/v3/extensions/limits_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package extensions - -import ( - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/extensions/limits" - - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" -) - -func TestLimits(t *testing.T) { - client, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - limits, err := limits.Get(client) - th.AssertNoErr(t, err) - - tools.PrintResource(t, limits) - - th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalVolumes, 0) - th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalSnapshots, 0) - th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalVolumeGigabytes, 0) - th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalBackups, 0) - th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalBackupGigabytes, 0) - th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalVolumesUsed, 0) - th.AssertIntLesserOrEqual(t, limits.Absolute.TotalVolumesUsed, limits.Absolute.MaxTotalVolumes) - th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalGigabytesUsed, 0) - th.AssertIntLesserOrEqual(t, limits.Absolute.TotalGigabytesUsed, limits.Absolute.MaxTotalVolumeGigabytes) - th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalSnapshotsUsed, 0) - th.AssertIntLesserOrEqual(t, limits.Absolute.TotalSnapshotsUsed, limits.Absolute.MaxTotalSnapshots) - th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalBackupsUsed, 0) - th.AssertIntLesserOrEqual(t, limits.Absolute.TotalBackupsUsed, limits.Absolute.MaxTotalBackups) - th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalBackupGigabytesUsed, 0) - th.AssertIntLesserOrEqual(t, limits.Absolute.TotalBackupGigabytesUsed, limits.Absolute.MaxTotalBackupGigabytes) -} From 29ba3d5c69343a3d3b673775cf988ede90bd82f9 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Tue, 8 Nov 2022 15:48:05 +0100 Subject: [PATCH 46/51] fix --- .../evs/extensions/schedulerstats/requests.go | 43 ------- .../evs/extensions/schedulerstats/results.go | 112 ------------------ .../evs/extensions/schedulerstats/urls.go | 7 -- 3 files changed, 162 deletions(-) delete mode 100644 openstack/evs/extensions/schedulerstats/requests.go delete mode 100644 openstack/evs/extensions/schedulerstats/results.go delete mode 100644 openstack/evs/extensions/schedulerstats/urls.go diff --git a/openstack/evs/extensions/schedulerstats/requests.go b/openstack/evs/extensions/schedulerstats/requests.go deleted file mode 100644 index 08d079be5..000000000 --- a/openstack/evs/extensions/schedulerstats/requests.go +++ /dev/null @@ -1,43 +0,0 @@ -package schedulerstats - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToStoragePoolsListQuery() (string, error) -} - -// ListOpts controls the view of data returned (e.g globally or per project) -// via tenant_id and the verbosity via detail. -type ListOpts struct { - // ID of the tenant to look up storage pools for. - TenantID string `q:"tenant_id"` - - // Whether to list extended details. - Detail bool `q:"detail"` -} - -// ToStoragePoolsListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToStoragePoolsListQuery() (string, error) { - q, err := golangsdk.BuildQueryString(opts) - return q.String(), err -} - -// List makes a request against the API to list storage pool information. -func List(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := storagePoolsListURL(client) - if opts != nil { - query, err := opts.ToStoragePoolsListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return StoragePoolPage{pagination.SinglePageBase(r)} - }) -} diff --git a/openstack/evs/extensions/schedulerstats/results.go b/openstack/evs/extensions/schedulerstats/results.go deleted file mode 100644 index 745e888e7..000000000 --- a/openstack/evs/extensions/schedulerstats/results.go +++ /dev/null @@ -1,112 +0,0 @@ -package schedulerstats - -import ( - "encoding/json" - "math" - "strconv" - - "github.com/opentelekomcloud/gophertelekomcloud/pagination" -) - -// Capabilities represents the information of an individual StoragePool. -type Capabilities struct { - // The following fields should be present in all storage drivers. - DriverVersion string `json:"driver_version"` - FreeCapacityGB float64 `json:"-"` - StorageProtocol string `json:"storage_protocol"` - TotalCapacityGB float64 `json:"-"` - VendorName string `json:"vendor_name"` - VolumeBackendName string `json:"volume_backend_name"` - - // The following fields are optional and may have empty values depending - // on the storage driver in use. - ReservedPercentage int64 `json:"reserved_percentage"` - LocationInfo string `json:"location_info"` - QoSSupport bool `json:"QoS_support"` - ProvisionedCapacityGB float64 `json:"provisioned_capacity_gb"` - MaxOverSubscriptionRatio string `json:"-"` - ThinProvisioningSupport bool `json:"thin_provisioning_support"` - ThickProvisioningSupport bool `json:"thick_provisioning_support"` - TotalVolumes int64 `json:"total_volumes"` - FilterFunction string `json:"filter_function"` - GoodnessFunction string `json:"goodness_function"` - Multiattach bool `json:"multiattach"` - SparseCopyVolume bool `json:"sparse_copy_volume"` - AllocatedCapacityGB float64 `json:"-"` -} - -// StoragePool represents an individual StoragePool retrieved from the -// schedulerstats API. -type StoragePool struct { - Name string `json:"name"` - Capabilities Capabilities `json:"capabilities"` -} - -func (r *Capabilities) UnmarshalJSON(b []byte) error { - type tmp Capabilities - var s struct { - tmp - AllocatedCapacityGB interface{} `json:"allocated_capacity_gb"` - FreeCapacityGB interface{} `json:"free_capacity_gb"` - MaxOverSubscriptionRatio interface{} `json:"max_over_subscription_ratio"` - TotalCapacityGB interface{} `json:"total_capacity_gb"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Capabilities(s.tmp) - - // Generic function to parse a capacity value which may be a numeric - // value, "unknown", or "infinite" - parseCapacity := func(capacity interface{}) float64 { - if capacity != nil { - switch capacity.(type) { - case float64: - return capacity.(float64) - case string: - if capacity.(string) == "infinite" { - return math.Inf(1) - } - } - } - return 0.0 - } - - r.AllocatedCapacityGB = parseCapacity(s.AllocatedCapacityGB) - r.FreeCapacityGB = parseCapacity(s.FreeCapacityGB) - r.TotalCapacityGB = parseCapacity(s.TotalCapacityGB) - - if s.MaxOverSubscriptionRatio != nil { - switch t := s.MaxOverSubscriptionRatio.(type) { - case float64: - r.MaxOverSubscriptionRatio = strconv.FormatFloat(t, 'f', -1, 64) - case string: - r.MaxOverSubscriptionRatio = t - } - } - - return nil -} - -// StoragePoolPage is a single page of all List results. -type StoragePoolPage struct { - pagination.SinglePageBase -} - -// IsEmpty satisfies the IsEmpty method of the Page interface. It returns true -// if a List contains no results. -func (page StoragePoolPage) IsEmpty() (bool, error) { - va, err := ExtractStoragePools(page) - return len(va) == 0, err -} - -// ExtractStoragePools takes a List result and extracts the collection of -// StoragePools returned by the API. -func ExtractStoragePools(p pagination.Page) ([]StoragePool, error) { - var s struct { - StoragePools []StoragePool `json:"pools"` - } - err := (p.(StoragePoolPage)).ExtractInto(&s) - return s.StoragePools, err -} diff --git a/openstack/evs/extensions/schedulerstats/urls.go b/openstack/evs/extensions/schedulerstats/urls.go deleted file mode 100644 index 3ecd22547..000000000 --- a/openstack/evs/extensions/schedulerstats/urls.go +++ /dev/null @@ -1,7 +0,0 @@ -package schedulerstats - -import "github.com/opentelekomcloud/gophertelekomcloud" - -func storagePoolsListURL(c *golangsdk.ServiceClient) string { - return c.ServiceURL("scheduler-stats", "get_pools") -} From 0e0b02ee9bcba8779e146aa0a24c88a7dbaedb36 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 9 Nov 2022 13:15:54 +0100 Subject: [PATCH 47/51] fix --- acceptance/openstack/common.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/common.go b/acceptance/openstack/common.go index 422ec86ad..eefc5387d 100644 --- a/acceptance/openstack/common.go +++ b/acceptance/openstack/common.go @@ -10,10 +10,10 @@ import ( "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/blockstorage/v2/volumes" "github.com/opentelekomcloud/gophertelekomcloud/openstack/common/extensions" "github.com/opentelekomcloud/gophertelekomcloud/openstack/compute/v2/extensions/secgroups" "github.com/opentelekomcloud/gophertelekomcloud/openstack/ecs/v1/cloudservers" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" "github.com/opentelekomcloud/gophertelekomcloud/openstack/imageservice/v2/images" "github.com/opentelekomcloud/gophertelekomcloud/openstack/networking/v1/subnets" th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" @@ -82,11 +82,11 @@ func CreateVolume(t *testing.T) *volumes.Volume { Name: tools.RandomString("test-vol-", 6), Size: 10, VolumeType: "SSD", - }).Extract() + }) th.AssertNoErr(t, err) err = golangsdk.WaitFor(300, func() (bool, error) { - volume, err := volumes.Get(client, vol.ID).Extract() + volume, err := volumes.Get(client, vol.ID) if err != nil { return false, err } @@ -106,7 +106,10 @@ func CreateVolume(t *testing.T) *volumes.Volume { func DeleteVolume(t *testing.T, id string) { client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - th.AssertNoErr(t, volumes.Delete(client, id, volumes.DeleteOpts{Cascade: true}).ExtractErr()) + th.AssertNoErr(t, volumes.Delete(client, volumes.DeleteOpts{ + VolumeId: id, + Cascade: true, + })) } const ( From d0cf3befc4145b5f0bf463250e0b60d7c840451d Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 4 Jan 2023 12:45:21 +0100 Subject: [PATCH 48/51] rm --- .../evs/extensions/volumeactions/results.go | 105 ------------------ 1 file changed, 105 deletions(-) delete mode 100644 openstack/evs/extensions/volumeactions/results.go diff --git a/openstack/evs/extensions/volumeactions/results.go b/openstack/evs/extensions/volumeactions/results.go deleted file mode 100644 index e02d2e32e..000000000 --- a/openstack/evs/extensions/volumeactions/results.go +++ /dev/null @@ -1,105 +0,0 @@ -package volumeactions - -import ( - "github.com/opentelekomcloud/gophertelekomcloud" -) - -// AttachResult contains the response body and error from an Attach request. -type AttachResult struct { - golangsdk.ErrResult -} - -// BeginDetachingResult contains the response body and error from a BeginDetach -// request. -type BeginDetachingResult struct { - golangsdk.ErrResult -} - -// DetachResult contains the response body and error from a Detach request. -type DetachResult struct { - golangsdk.ErrResult -} - -// UploadImageResult contains the response body and error from an UploadImage -// request. -type UploadImageResult struct { - golangsdk.Result -} - -// SetImageMetadataResult contains the response body and error from an SetImageMetadata -// request. -type SetImageMetadataResult struct { - golangsdk.ErrResult -} - -// SetBootableResult contains the response body and error from a SetBootable -// request. -type SetBootableResult struct { - golangsdk.ErrResult -} - -// ReserveResult contains the response body and error from a Reserve request. -type ReserveResult struct { - golangsdk.ErrResult -} - -// UnreserveResult contains the response body and error from an Unreserve -// request. -type UnreserveResult struct { - golangsdk.ErrResult -} - -// TerminateConnectionResult contains the response body and error from a -// TerminateConnection request. -type TerminateConnectionResult struct { - golangsdk.ErrResult -} - -// InitializeConnectionResult contains the response body and error from an -// InitializeConnection request. -type InitializeConnectionResult struct { - golangsdk.Result -} - -// ExtendSizeResult contains the response body and error from an ExtendSize request. -type ExtendSizeResult struct { - golangsdk.ErrResult -} - -// Extract will get the connection information out of the -// InitializeConnectionResult object. -// -// This will be a generic map[string]interface{} and the results will be -// dependent on the type of connection made. -func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) { - var s struct { - ConnectionInfo map[string]interface{} `json:"connection_info"` - } - err := r.ExtractInto(&s) - return s.ConnectionInfo, err -} - -// Extract will get an object with info about the uploaded image out of the -// UploadImageResult object. -func (r UploadImageResult) Extract() (VolumeImage, error) { - var s struct { - VolumeImage VolumeImage `json:"os-volume_upload_image"` - } - err := r.ExtractInto(&s) - return s.VolumeImage, err -} - -// ForceDeleteResult contains the response body and error from a ForceDelete request. -type ForceDeleteResult struct { - golangsdk.ErrResult -} - -// ChangeTypeResult contains the response body and error from an ChangeType request. -type ChangeTypeResult struct { - golangsdk.ErrResult -} - -// ReImageResult contains the response body and error from a ReImage request. -type ReImageResult struct { - golangsdk.ErrResult -} From 3d6c6b0b63c44dfeb15fced3042761fddd998bb6 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 4 Jan 2023 12:56:19 +0100 Subject: [PATCH 49/51] mv --- openstack/evs/v3/volumes/Create.go | 17 +++ openstack/evs/v3/volumes/Get.go | 100 +++++++++++++++++- openstack/evs/v3/volumes/Update.go | 40 +++++++ openstack/evs/v3/volumes/results.go | 155 ---------------------------- 4 files changed, 156 insertions(+), 156 deletions(-) delete mode 100644 openstack/evs/v3/volumes/results.go diff --git a/openstack/evs/v3/volumes/Create.go b/openstack/evs/v3/volumes/Create.go index 6bac108bb..8c9591efc 100644 --- a/openstack/evs/v3/volumes/Create.go +++ b/openstack/evs/v3/volumes/Create.go @@ -115,3 +115,20 @@ func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (*Volume, e raw, err := client.Post(client.ServiceURL("volumes"), b, nil, nil) return extra(err, raw) } + +// WaitForStatus will continually poll the resource, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { + return golangsdk.WaitFor(secs, func() (bool, error) { + current, err := Get(c, id) + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} diff --git a/openstack/evs/v3/volumes/Get.go b/openstack/evs/v3/volumes/Get.go index 09429c363..da2d8fc16 100644 --- a/openstack/evs/v3/volumes/Get.go +++ b/openstack/evs/v3/volumes/Get.go @@ -1,6 +1,13 @@ package volumes -import "github.com/opentelekomcloud/gophertelekomcloud" +import ( + "encoding/json" + "net/http" + "time" + + "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" +) // Get retrieves the Volume with the provided ID. func Get(client *golangsdk.ServiceClient, id string) (*Volume, error) { @@ -8,3 +15,94 @@ func Get(client *golangsdk.ServiceClient, id string) (*Volume, error) { raw, err := client.Get(client.ServiceURL("volumes", id), nil, nil) return extra(err, raw) } + +// Volume contains all the information associated with an OpenStack Volume. +type Volume struct { + // Unique identifier for the volume. + ID string `json:"id"` + // Specifies the disk URI. + Links []golangsdk.Link `json:"links"` + // Current status of the volume. + Status string `json:"status"` + // Size of the volume in GB. + Size int `json:"size"` + // AvailabilityZone is which availability zone the volume is in. + AvailabilityZone string `json:"availability_zone"` + // The date when this volume was created. + CreatedAt time.Time `json:"-"` + // The date when this volume was last updated + UpdatedAt time.Time `json:"-"` + // Instances onto which the volume is attached. + Attachments []Attachment `json:"attachments"` + // Human-readable display name for the volume. + Name string `json:"name"` + // Human-readable description for the volume. + Description string `json:"description"` + // Specifies the disk type. + // Currently, the value can be SSD, SAS, SATA, co-p1, uh-l1, GPSSD, or ESSD. + // SSD: specifies the ultra-high I/O disk type. + // SAS: specifies the high I/O disk type. + // SATA: specifies the common I/O disk type. + // co-p1: specifies the high I/O (performance-optimized I) disk type. + // uh-l1: specifies the ultra-high I/O (latency-optimized) disk type. + // GPSSD: specifies the general purpose SSD disk type. + // ESSD: specifies the extreme SSD disk type. + // Disks of the co-p1 and uh-l1 types are used exclusively for HPC ECSs and SAP HANA ECSs. + VolumeType string `json:"volume_type"` + // The ID of the snapshot from which the volume was created + SnapshotID string `json:"snapshot_id"` + // The ID of another block storage volume from which the current volume was created + // Currently, this field is not supported by EVS. + SourceVolID string `json:"source_volid"` + // The backup ID, from which the volume was restored + // This field is supported since 3.47 microversion + BackupID string `json:"backup_id"` + // Arbitrary key-value pairs defined by the user. + Metadata map[string]string `json:"metadata"` + // UserID is the id of the user who created the volume. + UserID string `json:"user_id"` + // Indicates whether this is a bootable volume. + Bootable string `json:"bootable"` + // Encrypted denotes if the volume is encrypted. + // Currently, this field is not supported by EVS. + Encrypted bool `json:"encrypted"` + // ReplicationStatus is the status of replication. + // Currently, this field is not supported by EVS. + ReplicationStatus string `json:"replication_status"` + // ConsistencyGroupID is the consistency group ID. + ConsistencyGroupID string `json:"consistencygroup_id"` + // Specifies whether the disk is shareable. + Multiattach bool `json:"multiattach"` + // Image metadata entries, only included for volumes that were created from an image, or from a snapshot of a volume originally created from an image. + VolumeImageMetadata map[string]string `json:"volume_image_metadata"` +} + +// UnmarshalJSON another unmarshalling function +func (r *Volume) UnmarshalJSON(b []byte) error { + type tmp Volume + var s struct { + tmp + CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Volume(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} + +func extra(err error, raw *http.Response) (*Volume, error) { + if err != nil { + return nil, err + } + + var res Volume + err = extract.IntoStructPtr(raw.Body, &res, "volume") + return &res, err +} diff --git a/openstack/evs/v3/volumes/Update.go b/openstack/evs/v3/volumes/Update.go index c3e5f7a82..a3d28e164 100644 --- a/openstack/evs/v3/volumes/Update.go +++ b/openstack/evs/v3/volumes/Update.go @@ -1,6 +1,9 @@ package volumes import ( + "encoding/json" + "time" + "github.com/opentelekomcloud/gophertelekomcloud" "github.com/opentelekomcloud/gophertelekomcloud/internal/build" ) @@ -39,3 +42,40 @@ func Update(client *golangsdk.ServiceClient, opts UpdateOpts) (*Volume, error) { }) return extra(err, raw) } + +// Attachment represents a Volume Attachment record +type Attachment struct { + // Specifies the time when the disk was attached. + // Time format: UTC YYYY-MM-DDTHH:MM:SS.XXXXXX + AttachedAt time.Time `json:"-"` + // Specifies the ID of the attachment information. + AttachmentID string `json:"attachment_id"` + // Specifies the device name. + Device string `json:"device"` + // Specifies the name of the physical host accommodating the server to which the disk is attached. + HostName string `json:"host_name"` + // Specifies the ID of the attached resource. + ID string `json:"id"` + // Specifies the ID of the server to which the disk is attached. + ServerID string `json:"server_id"` + // Specifies the disk ID. + VolumeID string `json:"volume_id"` +} + +// UnmarshalJSON is our unmarshalling helper +func (r *Attachment) UnmarshalJSON(b []byte) error { + type tmp Attachment + var s struct { + tmp + AttachedAt golangsdk.JSONRFC3339MilliNoZ `json:"attached_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Attachment(s.tmp) + + r.AttachedAt = time.Time(s.AttachedAt) + + return err +} diff --git a/openstack/evs/v3/volumes/results.go b/openstack/evs/v3/volumes/results.go deleted file mode 100644 index 8f1b724dc..000000000 --- a/openstack/evs/v3/volumes/results.go +++ /dev/null @@ -1,155 +0,0 @@ -package volumes - -import ( - "encoding/json" - "net/http" - "time" - - "github.com/opentelekomcloud/gophertelekomcloud" - "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" -) - -// Attachment represents a Volume Attachment record -type Attachment struct { - // Specifies the time when the disk was attached. - // Time format: UTC YYYY-MM-DDTHH:MM:SS.XXXXXX - AttachedAt time.Time `json:"-"` - // Specifies the ID of the attachment information. - AttachmentID string `json:"attachment_id"` - // Specifies the device name. - Device string `json:"device"` - // Specifies the name of the physical host accommodating the server to which the disk is attached. - HostName string `json:"host_name"` - // Specifies the ID of the attached resource. - ID string `json:"id"` - // Specifies the ID of the server to which the disk is attached. - ServerID string `json:"server_id"` - // Specifies the disk ID. - VolumeID string `json:"volume_id"` -} - -// UnmarshalJSON is our unmarshalling helper -func (r *Attachment) UnmarshalJSON(b []byte) error { - type tmp Attachment - var s struct { - tmp - AttachedAt golangsdk.JSONRFC3339MilliNoZ `json:"attached_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Attachment(s.tmp) - - r.AttachedAt = time.Time(s.AttachedAt) - - return err -} - -// Volume contains all the information associated with an OpenStack Volume. -type Volume struct { - // Unique identifier for the volume. - ID string `json:"id"` - // Specifies the disk URI. - Links []golangsdk.Link `json:"links"` - // Current status of the volume. - Status string `json:"status"` - // Size of the volume in GB. - Size int `json:"size"` - // AvailabilityZone is which availability zone the volume is in. - AvailabilityZone string `json:"availability_zone"` - // The date when this volume was created. - CreatedAt time.Time `json:"-"` - // The date when this volume was last updated - UpdatedAt time.Time `json:"-"` - // Instances onto which the volume is attached. - Attachments []Attachment `json:"attachments"` - // Human-readable display name for the volume. - Name string `json:"name"` - // Human-readable description for the volume. - Description string `json:"description"` - // Specifies the disk type. - // Currently, the value can be SSD, SAS, SATA, co-p1, uh-l1, GPSSD, or ESSD. - // SSD: specifies the ultra-high I/O disk type. - // SAS: specifies the high I/O disk type. - // SATA: specifies the common I/O disk type. - // co-p1: specifies the high I/O (performance-optimized I) disk type. - // uh-l1: specifies the ultra-high I/O (latency-optimized) disk type. - // GPSSD: specifies the general purpose SSD disk type. - // ESSD: specifies the extreme SSD disk type. - // Disks of the co-p1 and uh-l1 types are used exclusively for HPC ECSs and SAP HANA ECSs. - VolumeType string `json:"volume_type"` - // The ID of the snapshot from which the volume was created - SnapshotID string `json:"snapshot_id"` - // The ID of another block storage volume from which the current volume was created - // Currently, this field is not supported by EVS. - SourceVolID string `json:"source_volid"` - // The backup ID, from which the volume was restored - // This field is supported since 3.47 microversion - BackupID string `json:"backup_id"` - // Arbitrary key-value pairs defined by the user. - Metadata map[string]string `json:"metadata"` - // UserID is the id of the user who created the volume. - UserID string `json:"user_id"` - // Indicates whether this is a bootable volume. - Bootable string `json:"bootable"` - // Encrypted denotes if the volume is encrypted. - // Currently, this field is not supported by EVS. - Encrypted bool `json:"encrypted"` - // ReplicationStatus is the status of replication. - // Currently, this field is not supported by EVS. - ReplicationStatus string `json:"replication_status"` - // ConsistencyGroupID is the consistency group ID. - ConsistencyGroupID string `json:"consistencygroup_id"` - // Specifies whether the disk is shareable. - Multiattach bool `json:"multiattach"` - // Image metadata entries, only included for volumes that were created from an image, or from a snapshot of a volume originally created from an image. - VolumeImageMetadata map[string]string `json:"volume_image_metadata"` -} - -// UnmarshalJSON another unmarshalling function -func (r *Volume) UnmarshalJSON(b []byte) error { - type tmp Volume - var s struct { - tmp - CreatedAt golangsdk.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt golangsdk.JSONRFC3339MilliNoZ `json:"updated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Volume(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) - - return err -} - -func extra(err error, raw *http.Response) (*Volume, error) { - if err != nil { - return nil, err - } - - var res Volume - err = extract.IntoStructPtr(raw.Body, &res, "volume") - return &res, err -} - -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(c *golangsdk.ServiceClient, id, status string, secs int) error { - return golangsdk.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id) - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} From fe4fade7e53dbcc675a931f000357871a45e8c39 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Wed, 4 Jan 2023 17:27:40 +0100 Subject: [PATCH 50/51] AvailabilityZone --- acceptance/openstack/evs/v3/blockstorage.go | 8 +++++--- .../openstack/evs/v3/extensions/schedulerhints_test.go | 5 +++-- .../openstack/evs/v3/extensions/schedulerstats_test.go | 2 ++ acceptance/openstack/evs/v3/extensions/services_test.go | 2 ++ .../openstack/evs/v3/extensions/volumeactions_test.go | 2 +- openstack/evs/v3/volumes/Create.go | 4 ++-- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/evs/v3/blockstorage.go b/acceptance/openstack/evs/v3/blockstorage.go index 70d9fc88e..c1408740c 100644 --- a/acceptance/openstack/evs/v3/blockstorage.go +++ b/acceptance/openstack/evs/v3/blockstorage.go @@ -4,6 +4,7 @@ import ( "testing" golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" "github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/snapshots" "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" @@ -51,9 +52,10 @@ func CreateVolume(t *testing.T, client *golangsdk.ServiceClient) (*volumes.Volum t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ - Size: 1, - Name: volumeName, - Description: volumeDescription, + Size: 1, + Name: volumeName, + Description: volumeDescription, + AvailabilityZone: clients.EnvOS.GetEnv("AVAILABILITY_ZONE"), } volume, err := volumes.Create(client, createOpts) diff --git a/acceptance/openstack/evs/v3/extensions/schedulerhints_test.go b/acceptance/openstack/evs/v3/extensions/schedulerhints_test.go index 58e1cfa53..70e345e43 100644 --- a/acceptance/openstack/evs/v3/extensions/schedulerhints_test.go +++ b/acceptance/openstack/evs/v3/extensions/schedulerhints_test.go @@ -17,8 +17,9 @@ func TestSchedulerHints(t *testing.T) { volumeName := tools.RandomString("ACPTTEST", 16) createOpts := volumes.CreateOpts{ - Size: 1, - Name: volumeName, + Size: 1, + Name: volumeName, + AvailabilityZone: clients.EnvOS.GetEnv("AVAILABILITY_ZONE"), } volume1, err := volumes.Create(client, createOpts) diff --git a/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go b/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go index ba746fd7d..ddb578af5 100644 --- a/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go +++ b/acceptance/openstack/evs/v3/extensions/schedulerstats_test.go @@ -10,6 +10,8 @@ import ( ) func TestSchedulerStatsList(t *testing.T) { + t.Skip("The API does not exist or has not been published in the environment") + blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/evs/v3/extensions/services_test.go b/acceptance/openstack/evs/v3/extensions/services_test.go index ab923d8d6..46fd97be7 100644 --- a/acceptance/openstack/evs/v3/extensions/services_test.go +++ b/acceptance/openstack/evs/v3/extensions/services_test.go @@ -11,6 +11,8 @@ import ( ) func TestServicesList(t *testing.T) { + t.Skip("The API does not exist or has not been published in the environment") + blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/evs/v3/extensions/volumeactions_test.go b/acceptance/openstack/evs/v3/extensions/volumeactions_test.go index 924543f4f..ce8ef5ed6 100644 --- a/acceptance/openstack/evs/v3/extensions/volumeactions_test.go +++ b/acceptance/openstack/evs/v3/extensions/volumeactions_test.go @@ -40,7 +40,7 @@ func TestVolumeActionsAttachCreateDestroy(t *testing.T) { server, err := compute.CreateServer(t, computeClient) th.AssertNoErr(t, err) - defer compute.DeleteServer(t, computeClient, server) + t.Cleanup(func() { compute.DeleteServer(t, computeClient, server) }) volume, err := blockstorageV3.CreateVolume(t, blockClient) th.AssertNoErr(t, err) diff --git a/openstack/evs/v3/volumes/Create.go b/openstack/evs/v3/volumes/Create.go index 8c9591efc..3d721a070 100644 --- a/openstack/evs/v3/volumes/Create.go +++ b/openstack/evs/v3/volumes/Create.go @@ -23,9 +23,9 @@ type CreateOpts struct { // // This parameter is mandatory when you create the disk from an image. // Ensure that the disk size is greater than or equal to the minimum disk capacity required by min_disk in the image attributes. - Size int `json:"size,omitempty"` + Size int `json:"size" required:"true"` // Specifies the AZ where you want to create the disk. If the AZ does not exist, the disk will fail to create. - AvailabilityZone string `json:"availability_zone"` + AvailabilityZone string `json:"availability_zone" required:"true"` // ConsistencyGroupID is the ID of a consistency group // Currently, this function is not supported. ConsistencyGroupID string `json:"consistencygroup_id,omitempty"` From 5a313a018054503b4d78cc4c08c9e03c4c6f5fff Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Thu, 5 Jan 2023 13:30:52 +0100 Subject: [PATCH 51/51] VolumeTenantExt --- acceptance/openstack/compute/v2/helper.go | 1 + .../evs/v3/extensions/volumeactions_test.go | 3 ++ .../evs/v3/extensions/volumetenants_test.go | 45 ------------------- openstack/compute/v2/images/requests.go | 1 + openstack/evs/v3/volumes/Get.go | 2 + openstack/evs/v3/volumes/List.go | 2 +- 6 files changed, 8 insertions(+), 46 deletions(-) delete mode 100644 acceptance/openstack/evs/v3/extensions/volumetenants_test.go diff --git a/acceptance/openstack/compute/v2/helper.go b/acceptance/openstack/compute/v2/helper.go index 2d77070ce..50f1b6af2 100644 --- a/acceptance/openstack/compute/v2/helper.go +++ b/acceptance/openstack/compute/v2/helper.go @@ -28,6 +28,7 @@ func CreateServer(t *testing.T, client *golangsdk.ServiceClient) (*servers.Serve t.Skip("OS_NETWORK_ID env var is missing but ECS test requires using existing network") } + // TODO: API discarded and not working. imageID, err := images.IDFromName(client, "Standard_Debian_10_latest") th.AssertNoErr(t, err) diff --git a/acceptance/openstack/evs/v3/extensions/volumeactions_test.go b/acceptance/openstack/evs/v3/extensions/volumeactions_test.go index ce8ef5ed6..d9cfb418c 100644 --- a/acceptance/openstack/evs/v3/extensions/volumeactions_test.go +++ b/acceptance/openstack/evs/v3/extensions/volumeactions_test.go @@ -32,6 +32,9 @@ func TestVolumeActionsUploadImageDestroy(t *testing.T) { } func TestVolumeActionsAttachCreateDestroy(t *testing.T) { + // TODO + t.Skip("images.ListDetail discarded and not working.") + blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/evs/v3/extensions/volumetenants_test.go b/acceptance/openstack/evs/v3/extensions/volumetenants_test.go deleted file mode 100644 index 597d62ca7..000000000 --- a/acceptance/openstack/evs/v3/extensions/volumetenants_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package extensions - -import ( - "testing" - - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/extensions/volumetenants" - - "github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients" - blockstorage "github.com/opentelekomcloud/gophertelekomcloud/acceptance/openstack/evs/v3" - "github.com/opentelekomcloud/gophertelekomcloud/openstack/evs/v3/volumes" - th "github.com/opentelekomcloud/gophertelekomcloud/testhelper" -) - -func TestVolumeTenants(t *testing.T) { - type volumeWithTenant struct { - volumes.Volume - volumetenants.VolumeTenantExt - } - - var allVolumes []volumeWithTenant - - client, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - listOpts := volumes.ListOpts{ - Name: "I SHOULD NOT EXIST", - } - allPages, err := volumes.List(client, listOpts).AllPages() - th.AssertNoErr(t, err) - - err = volumes.ExtractVolumesInto(allPages, &allVolumes) - th.AssertNoErr(t, err) - th.AssertEquals(t, 0, len(allVolumes)) - - volume1, err := blockstorage.CreateVolume(t, client) - th.AssertNoErr(t, err) - t.Cleanup(func() { blockstorage.DeleteVolume(t, client, volume1) }) - - allPages, err = volumes.List(client, volumes.ListOpts{}).AllPages() - th.AssertNoErr(t, err) - - err = volumes.ExtractVolumesInto(allPages, &allVolumes) - th.AssertNoErr(t, err) - th.AssertEquals(t, true, len(allVolumes) > 0) -} diff --git a/openstack/compute/v2/images/requests.go b/openstack/compute/v2/images/requests.go index 89e1489b2..f107a7ae4 100644 --- a/openstack/compute/v2/images/requests.go +++ b/openstack/compute/v2/images/requests.go @@ -46,6 +46,7 @@ func (opts ListOpts) ToImageListQuery() (string, error) { } // ListDetail enumerates the available images. +// TODO: API discarded and not working. func ListDetail(client *golangsdk.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listDetailURL(client) if opts != nil { diff --git a/openstack/evs/v3/volumes/Get.go b/openstack/evs/v3/volumes/Get.go index da2d8fc16..576c0c91b 100644 --- a/openstack/evs/v3/volumes/Get.go +++ b/openstack/evs/v3/volumes/Get.go @@ -75,6 +75,8 @@ type Volume struct { Multiattach bool `json:"multiattach"` // Image metadata entries, only included for volumes that were created from an image, or from a snapshot of a volume originally created from an image. VolumeImageMetadata map[string]string `json:"volume_image_metadata"` + // TenantID is the id of the project that owns the volume. + TenantID string `json:"os-vol-tenant-attr:tenant_id"` } // UnmarshalJSON another unmarshalling function diff --git a/openstack/evs/v3/volumes/List.go b/openstack/evs/v3/volumes/List.go index ebd8dbc9e..efcb590c2 100644 --- a/openstack/evs/v3/volumes/List.go +++ b/openstack/evs/v3/volumes/List.go @@ -82,5 +82,5 @@ func ExtractVolumes(r pagination.Page) ([]Volume, error) { // ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes func ExtractVolumesInto(r pagination.Page, v interface{}) error { - return extract.IntoSlicePtr(r.(VolumePage).Result.BodyReader(), v, "volumes") + return extract.IntoSlicePtr(r.(VolumePage).BodyReader(), v, "volumes") }