diff --git a/volume/v3/volume.go b/volume/v3/volume.go new file mode 100644 index 0000000..6b71835 --- /dev/null +++ b/volume/v3/volume.go @@ -0,0 +1,284 @@ +// Copyright (c) 2016 Huawei Technologies Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +/* +Package volume implements a client library for accessing OpenStack Volume service + +The CRUD operation of volumes can be retrieved using the api. Right now only + +Show and List methods can work. + +*/ + +package v3 + +import ( + "encoding/json" + "errors" + "io/ioutil" + "net/http" + "net/url" + + "git.openstack.org/openstack/golang-client.git/openstack" + "git.openstack.org/openstack/golang-client.git/util" +) + +type Service struct { + Session openstack.Session + Client http.Client + URL string +} + +type RequestBody struct { + // The volume name [OPTIONAL] + Name string `json:"name"` + // The size of the volume, in gibibytes (GiB) [REQUIRED] + Size int `json:"size"` +} + +type Body struct { + VolumeBody RequestBody `json:"volume"` +} + +// Response is a structure for all properties of +// an volume for a non detailed query +type Response struct { + ID string `json:"id"` + Name string `json:"name"` + + Consistencygroup_id string `json:"consistencygroup_id"` +} + +// DetailResponse is a structure for all properties of +// an volume for a detailed query +type DetailResponse struct { + ID string `json:"id"` + Attachments []map[string]string `json:"attachments"` + Links []map[string]string `json:"links"` + Metadata map[string]string `json:"metadata"` + Protected bool `json:"protected"` + Status string `json:"status"` + MigrationStatus string `json:"migration_status"` + UserID string `json:"user_id"` + Encrypted bool `json:"encrypted"` + Multiattach bool `json:"multiattach"` + CreatedAt util.RFC8601DateTime `json:"created_at"` + Description string `json:"description"` + Volume_type string `json:"volume_type"` + Name string `json:"name"` + Source_volid string `json:"source_volid"` + Snapshot_id string `json:"snapshot_id"` + Size int64 `json:"size"` + + Aavailability_zone string `json:"availability_zone"` + Rreplication_status string `json:"replication_status"` + Consistencygroup_id string `json:"consistencygroup_id"` +} + +type VolumeResponse struct { + Volume Response `json:"volume"` +} + +type VolumesResponse struct { + Volumes []Response `json:"volumes"` +} + +type DetailVolumeResponse struct { + DetailVolume DetailResponse `json:"volume"` +} + +type DetailVolumesResponse struct { + DetailVolumes []DetailResponse `json:"volumes"` +} + +func (volumeService Service) Create(reqBody *Body) (Response, error) { + return volumeService.createVolume(reqBody) +} + +func (volumeService Service) createVolume(reqBody *Body) (Response, error) { + nullResponse := Response{} + + reqURL, err := url.Parse(volumeService.URL) + if err != nil { + return nullResponse, err + } + urlPostFix := "/volumes" + reqURL.Path += urlPostFix + + var headers http.Header = http.Header{} + headers.Set("Content-Type", "application/json") + body, _ := json.Marshal(reqBody) + resp, err := volumeService.Session.Post(reqURL.String(), nil, &headers, &body) + if err != nil { + return nullResponse, err + } + + err = util.CheckHTTPResponseStatusCode(resp) + if err != nil { + return nullResponse, err + } + + rbody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nullResponse, errors.New("aaa") + } + + volumeResponse := new(VolumeResponse) + if err = json.Unmarshal(rbody, volumeResponse); err != nil { + return nullResponse, err + } + return volumeResponse.Volume, nil +} + +func (volumeService Service) Show(id string) (Response, error) { + return volumeService.getVolume(id) +} + +func (volumeService Service) getVolume(id string) (Response, error) { + nullResponse := Response{} + + reqURL, err := url.Parse(volumeService.URL) + if err != nil { + return nullResponse, err + } + urlPostFix := "/volumes" + "/" + id + reqURL.Path += urlPostFix + + var headers http.Header = http.Header{} + headers.Set("Content-Type", "application/json") + resp, err := volumeService.Session.Get(reqURL.String(), nil, &headers) + if err != nil { + return nullResponse, err + } + + err = util.CheckHTTPResponseStatusCode(resp) + if err != nil { + return nullResponse, err + } + + rbody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nullResponse, errors.New("aaa") + } + + volumeResponse := new(VolumeResponse) + if err = json.Unmarshal(rbody, volumeResponse); err != nil { + return nullResponse, err + } + return volumeResponse.Volume, nil +} + +func (volumeService Service) List() ([]Response, error) { + return volumeService.getAllVolumes() +} + +func (volumeService Service) getAllVolumes() ([]Response, error) { + nullResponses := make([]Response, 0) + + reqURL, err := url.Parse(volumeService.URL) + if err != nil { + return nullResponses, err + } + urlPostFix := "/volumes" + reqURL.Path += urlPostFix + + var headers http.Header = http.Header{} + headers.Set("Content-Type", "application/json") + resp, err := volumeService.Session.Get(reqURL.String(), nil, &headers) + if err != nil { + return nullResponses, err + } + + err = util.CheckHTTPResponseStatusCode(resp) + if err != nil { + return nullResponses, err + } + + rbody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nullResponses, errors.New("aaa") + } + + volumesResponse := new(VolumesResponse) + if err = json.Unmarshal(rbody, volumesResponse); err != nil { + return nullResponses, err + } + return volumesResponse.Volumes, nil +} + +func (volumeService Service) Update(id string, reqBody *Body) (Response, error) { + return volumeService.updateVolume(id, reqBody) +} + +func (volumeService Service) updateVolume(id string, reqBody *Body) (Response, error) { + nullResponse := Response{} + + reqURL, err := url.Parse(volumeService.URL) + if err != nil { + return nullResponse, err + } + urlPostFix := "/volumes" + "/" + id + reqURL.Path += urlPostFix + + var headers http.Header = http.Header{} + headers.Set("Content-Type", "application/json") + body, _ := json.Marshal(reqBody) + resp, err := volumeService.Session.Put(reqURL.String(), nil, &headers, &body) + if err != nil { + return nullResponse, err + } + + err = util.CheckHTTPResponseStatusCode(resp) + if err != nil { + return nullResponse, err + } + + rbody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nullResponse, errors.New("aaa") + } + + volumeResponse := new(VolumeResponse) + if err = json.Unmarshal(rbody, volumeResponse); err != nil { + return nullResponse, err + } + return volumeResponse.Volume, nil +} + +func (volumeService Service) Delete(id string) error { + return volumeService.deleteVolume(id) +} + +func (volumeService Service) deleteVolume(id string) error { + reqURL, err := url.Parse(volumeService.URL) + if err != nil { + return err + } + urlPostFix := "/volumes" + "/" + id + reqURL.Path += urlPostFix + + var headers http.Header = http.Header{} + headers.Set("Content-Type", "application/json") + resp, err := volumeService.Session.Delete(reqURL.String(), nil, &headers) + if err != nil { + return err + } + + err = util.CheckHTTPResponseStatusCode(resp) + if err != nil { + return err + } + + return nil +} diff --git a/volume/v3/volume_test.go b/volume/v3/volume_test.go new file mode 100644 index 0000000..10a5ba5 --- /dev/null +++ b/volume/v3/volume_test.go @@ -0,0 +1,204 @@ +// Copyright (c) 2016 Huawei Technologies Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +/* +Package volume implements a client library for accessing OpenStack Volume service + +The CRUD operation of volumes can be retrieved using the api. + +*/ + +package v3_test + +import ( + "errors" + "net/http" + "strings" + "testing" + + "git.openstack.org/openstack/golang-client.git/openstack" + "git.openstack.org/openstack/golang-client.git/testUtil" + "git.openstack.org/openstack/golang-client.git/volume/v3" +) + +var tokn = "ae5aebe5-6a5d-4a40-840a-9736a067aff4" + +/* +func TestCreateVolume(t *testing.T) { + anon := func(volumeService *v3.Service) { + requestBody := v3.RequestBody{100, "myvol1"} + volume, err := volumeService.Create(&requestBody) + if err != nil { + t.Error(err) + } + + expectedVolume := v3.Response{ + Name: "myvol1", + ID: "f5fc9874-fc89-4814-a358-23ba83a6115f", + Links: []map[string]string{{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "self"}, + {"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "bookmark"}}} + testUtil.Equals(t, expectedVolume, volume) + } + + //testCreateVolumeServiceAction(t, "volumes", sampleVolumesData, anon) +} +*/ + +func TestGetVolume(t *testing.T) { + anon := func(volumeService *v3.Service) { + volID := "f5fc9874-fc89-4814-a358-23ba83a6115f" + volume, err := volumeService.Show(volID) + if err != nil { + t.Error(err) + } + + expectedVolume := v3.Response{ + Name: "myvol1", + ID: "f5fc9874-fc89-4814-a358-23ba83a6115f", + Links: []map[string]string{{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "self"}, + {"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "bookmark"}}} + testUtil.Equals(t, expectedVolume, volume) + } + + testGetVolumeServiceAction(t, "f5fc9874-fc89-4814-a358-23ba83a6115f", sampleVolumeData, anon) +} + +func testGetVolumeServiceAction(t *testing.T, uriEndsWith string, testData string, volumeServiceAction func(*v3.Service)) { + anon := func(req *http.Request) { + reqURL := req.URL.String() + if !strings.HasSuffix(reqURL, uriEndsWith) { + t.Error(errors.New("Incorrect url created, expected:" + uriEndsWith + " at the end, actual url:" + reqURL)) + } + } + apiServer := testUtil.CreateGetJSONTestRequestServer(t, tokn, testData, anon) + defer apiServer.Close() + + auth := openstack.AuthToken{ + Access: openstack.AccessType{ + Token: openstack.Token{ + ID: tokn, + }, + }, + } + sess, _ := openstack.NewSession(http.DefaultClient, auth, nil) + volumeService := v3.Service{ + Session: *sess, + URL: apiServer.URL, + } + volumeServiceAction(&volumeService) +} + +func TestGetAllVolumes(t *testing.T) { + anon := func(volumeService *v3.Service) { + volumes, err := volumeService.List() + if err != nil { + t.Error(err) + } + + expectedVolume := v3.Response{ + Name: "myvol1", + ID: "f5fc9874-fc89-4814-a358-23ba83a6115f", + Links: []map[string]string{{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "self"}, + {"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "bookmark"}}} + testUtil.Equals(t, expectedVolume, volumes[0]) + } + + testGetAllVolumesServiceAction(t, "volumes", sampleVolumesData, anon) +} + +func testGetAllVolumesServiceAction(t *testing.T, uriEndsWith string, testData string, volumeServiceAction func(*v3.Service)) { + anon := func(req *http.Request) { + reqURL := req.URL.String() + if !strings.HasSuffix(reqURL, uriEndsWith) { + t.Error(errors.New("Incorrect url created, expected:" + uriEndsWith + " at the end, actual url:" + reqURL)) + } + } + apiServer := testUtil.CreateGetJSONTestRequestServer(t, tokn, testData, anon) + defer apiServer.Close() + + auth := openstack.AuthToken{ + Access: openstack.AccessType{ + Token: openstack.Token{ + ID: tokn, + }, + }, + } + sess, _ := openstack.NewSession(http.DefaultClient, auth, nil) + volumeService := v3.Service{ + Session: *sess, + URL: apiServer.URL, + } + volumeServiceAction(&volumeService) +} + +/* +func TestDeleteVolume(t *testing.T) { + anon := func(volumeService *v3.Service) { + volID := "f5fc9874-fc89-4814-a358-23ba83a6115f" + _, err := volumeService.Delete(volID) + if err != nil { + t.Error(err) + } + volume, err := volumeService.Show(volID) + + expectedVolume := v3.Response{} + testUtil.Equals(t, expectedVolume, volume) + } + + testDeleteVolumeServiceAction(t, "f5fc9874-fc89-4814-a358-23ba83a6115f", anon) +} + +func testDeleteVolumeServiceAction(t *testing.T, uriEndsWith string, volumeServiceAction func(*v3.Service)) { + apiServer := testUtil.CreateDeleteTestRequestServer(t, tokn, uriEndsWith) + defer apiServer.Close() + + auth := openstack.AuthToken{ + Access: openstack.AccessType{ + Token: openstack.Token{ + ID: tokn, + }, + }, + } + sess, _ := openstack.NewSession(http.DefaultClient, auth, nil) + volumeService := v3.Service{ + Session: *sess, + URL: apiServer.URL, + } + volumeServiceAction(&volumeService) +} +*/ + +var sampleVolumeData = `{ + "name":"myvol1", + "id":"f5fc9874-fc89-4814-a358-23ba83a6115f", + "links":[{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "self"}, + {"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "bookmark"}] +}` + +var sampleVolumesData = `{ + "volumes":[ + { + "name":"myvol1", + "id":"f5fc9874-fc89-4814-a358-23ba83a6115f", + "links":[{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "self"}, + {"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "bookmark"}] + }, + { + "name":"myvol2", + "id":"60055a0a-2451-4d78-af9c-f2302150602f", + "links":[{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/60055a0a-2451-4d78-af9c-f2302150602f", "rel": "self"}, + {"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/60055a0a-2451-4d78-af9c-f2302150602f", "rel": "bookmark"}] + } + ] +}`