From d277c84f9fe8e0e483d13d065a1a428b851e53c3 Mon Sep 17 00:00:00 2001 From: jerry Date: Fri, 30 Nov 2018 10:47:55 +0800 Subject: [PATCH 1/7] Fixed some bugs in devsds --- script/devsds/bootstrap.sh | 9 +++++++-- script/devsds/install.sh | 12 ++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/script/devsds/bootstrap.sh b/script/devsds/bootstrap.sh index 9dff79858..d9938aaa1 100755 --- a/script/devsds/bootstrap.sh +++ b/script/devsds/bootstrap.sh @@ -81,11 +81,16 @@ if [ ! -d ${OPENSDS_DIR} ]; then git clone https://github.com/opensds/opensds.git -b master fi +# make sure 'make' has been installed. +if [[ -z "$(which make)" ]]; then + log "Installing make ..." + sudo apt-get install make -y +fi + cd ${OPENSDS_DIR} if [ ! -d ${OPENSDS_DIR}/build ]; then log "Building OpenSDS ..." - sudo apt-get update > /dev/null - sudo apt-get install librados-dev librbd-dev -y > /dev/null + make ubuntu-dev-setup make fi diff --git a/script/devsds/install.sh b/script/devsds/install.sh index 3c3ea95ff..376e00741 100755 --- a/script/devsds/install.sh +++ b/script/devsds/install.sh @@ -108,6 +108,18 @@ find $LOGFILE_DIR -maxdepth 1 -name $LOGFILE_NAME.\* -mtime +$LOGDAYS -exec rm { LOGFILE=$LOGFILE.${CURRENT_LOG_TIME} SUMFILE=$LOGFILE.summary.${CURRENT_LOG_TIME} +# Before set log output, make sure python has already been installed. +if [[ -z "$(which python)" ]]; then + python_path=${python_path:-} + test -n "$(which python2)" && python_path=$(which python2) + test -n "$(which python3)" && python_path=$(which python3) + if [[ -z $python_path ]]; then + log_error "Can not find python, please install it." + exit 2 + fi + ln -s $python_path /usr/bin/python +fi + # Set fd 3 to a copy of stdout. So we can set fd 1 without losing # stdout later. exec 3>&1 From 90102816ecbcc8b4178d9b8b2767be25bda279fe Mon Sep 17 00:00:00 2001 From: Xing Yang Date: Tue, 4 Dec 2018 19:33:52 -0500 Subject: [PATCH 2/7] Removes credentials from the code Fixes #562 --- pkg/db/drivers/mysql/mysql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/db/drivers/mysql/mysql.go b/pkg/db/drivers/mysql/mysql.go index 5a27546df..3fc372f63 100755 --- a/pkg/db/drivers/mysql/mysql.go +++ b/pkg/db/drivers/mysql/mysql.go @@ -29,7 +29,7 @@ var c = &client{} // Init func Init(driver, credential string) *client { - // driver equals "mysql", and credential is "krej:Wh199582@tcp(100.64.128.40:3306)/db" + // driver equals "mysql" db, err := sql.Open(driver, credential) if err != nil { db.Close() From 2a2983167332acce669fd99be877441e89d1c3e4 Mon Sep 17 00:00:00 2001 From: leonwanghui Date: Wed, 5 Dec 2018 14:30:15 +0800 Subject: [PATCH 3/7] Remove cindercompatibleapi folder and update travis.yml --- .travis.yml | 3 +- contrib/cindercompatibleapi/README.md | 2 - contrib/cindercompatibleapi/api/apiversion.go | 63 -- .../api/apiversion_test.go | 68 -- contrib/cindercompatibleapi/api/attachment.go | 217 ------- .../api/attachment_test.go | 318 ---------- contrib/cindercompatibleapi/api/router.go | 180 ------ contrib/cindercompatibleapi/api/snapshot.go | 222 ------- .../cindercompatibleapi/api/snapshot_test.go | 351 ----------- contrib/cindercompatibleapi/api/volume.go | 369 ----------- .../cindercompatibleapi/api/volume_test.go | 419 ------------- contrib/cindercompatibleapi/api/volumetype.go | 415 ------------- .../api/volumetype_test.go | 581 ------------------ .../converter/apiversion.go | 71 --- .../converter/attachment.go | 275 --------- .../cindercompatibleapi/converter/snapshot.go | 279 --------- .../cindercompatibleapi/converter/volume.go | 456 -------------- .../converter/volumetype.go | 342 ----------- contrib/cindercompatibleapi/main.go | 45 -- 19 files changed, 1 insertion(+), 4675 deletions(-) delete mode 100644 contrib/cindercompatibleapi/README.md delete mode 100644 contrib/cindercompatibleapi/api/apiversion.go delete mode 100644 contrib/cindercompatibleapi/api/apiversion_test.go delete mode 100644 contrib/cindercompatibleapi/api/attachment.go delete mode 100644 contrib/cindercompatibleapi/api/attachment_test.go delete mode 100644 contrib/cindercompatibleapi/api/router.go delete mode 100644 contrib/cindercompatibleapi/api/snapshot.go delete mode 100644 contrib/cindercompatibleapi/api/snapshot_test.go delete mode 100644 contrib/cindercompatibleapi/api/volume.go delete mode 100644 contrib/cindercompatibleapi/api/volume_test.go delete mode 100644 contrib/cindercompatibleapi/api/volumetype.go delete mode 100644 contrib/cindercompatibleapi/api/volumetype_test.go delete mode 100644 contrib/cindercompatibleapi/converter/apiversion.go delete mode 100644 contrib/cindercompatibleapi/converter/attachment.go delete mode 100644 contrib/cindercompatibleapi/converter/snapshot.go delete mode 100644 contrib/cindercompatibleapi/converter/volume.go delete mode 100644 contrib/cindercompatibleapi/converter/volumetype.go delete mode 100644 contrib/cindercompatibleapi/main.go diff --git a/.travis.yml b/.travis.yml index 5fd1ac82c..04579884a 100755 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ -dist: trusty +dist: xenial sudo: required -group: deprecated-2017Q4 language: go go_import_path: github.com/opensds/opensds diff --git a/contrib/cindercompatibleapi/README.md b/contrib/cindercompatibleapi/README.md deleted file mode 100644 index 93c3f7552..000000000 --- a/contrib/cindercompatibleapi/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# cinder-compatible api -Cinder-compatible api receives cinder api request and convert it to opensds api. \ No newline at end of file diff --git a/contrib/cindercompatibleapi/api/apiversion.go b/contrib/cindercompatibleapi/api/apiversion.go deleted file mode 100644 index bf77b3ca5..000000000 --- a/contrib/cindercompatibleapi/api/apiversion.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS northbound service. - -*/ - -package api - -import ( - "encoding/json" - "fmt" - "net/http" - - "github.com/astaxie/beego" - log "github.com/golang/glog" - "github.com/opensds/opensds/contrib/cindercompatibleapi/converter" - "github.com/opensds/opensds/pkg/model" -) - -// VersionPortal ... -type VersionPortal struct { - beego.Controller -} - -// ListAllAPIVersions ... -func (portal *VersionPortal) ListAllAPIVersions() { - NewClient(portal.Ctx) - versions, err := opensdsClient.ListVersions() - if err != nil { - reason := fmt.Sprintf("List All Api Versions failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ListAllAPIVersionsResp(versions) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("List All Api Versions, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusMultipleChoices) - portal.Ctx.Output.Body(body) - return -} diff --git a/contrib/cindercompatibleapi/api/apiversion_test.go b/contrib/cindercompatibleapi/api/apiversion_test.go deleted file mode 100644 index fdb6b6eab..000000000 --- a/contrib/cindercompatibleapi/api/apiversion_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2018 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 api - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "reflect" - "testing" - - "github.com/astaxie/beego" - c "github.com/opensds/opensds/client" - "github.com/opensds/opensds/contrib/cindercompatibleapi/converter" -) - -func init() { - beego.Router("/", &VersionPortal{}, - "get:ListAllAPIVersions") - - opensdsClient = c.NewFakeClient(&c.Config{Endpoint: c.TestEp}) -} - -//////////////////////////////////////////////////////////////////////////////// -// Tests for Version // -//////////////////////////////////////////////////////////////////////////////// -func TestListAllAPIVersions(t *testing.T) { - r, _ := http.NewRequest("GET", "/", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ListAllAPIVersionsRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - //fmt.Println(string(w.Body.Bytes())) - expectedJSON := ` - { - "versions": [{ - "status": "CURRENT", - "updated": "2017-07-10T14:36:58.014Z", - "min_version": "3.0", - "id": "v3.0" - }] - }` - - var expected converter.ListAllAPIVersionsRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusMultipleChoices { - t.Errorf("Expected %v, actual %v", http.StatusMultipleChoices, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} diff --git a/contrib/cindercompatibleapi/api/attachment.go b/contrib/cindercompatibleapi/api/attachment.go deleted file mode 100644 index 361086761..000000000 --- a/contrib/cindercompatibleapi/api/attachment.go +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS northbound service. - -*/ - -package api - -import ( - "encoding/json" - "fmt" - "net/http" - - "github.com/astaxie/beego" - log "github.com/golang/glog" - "github.com/opensds/opensds/contrib/cindercompatibleapi/converter" - "github.com/opensds/opensds/pkg/model" -) - -// AttachmentPortal ... -type AttachmentPortal struct { - beego.Controller -} - -// DeleteAttachment ... -func (portal *AttachmentPortal) DeleteAttachment() { - id := portal.Ctx.Input.Param(":attachmentId") - attachment := model.VolumeAttachmentSpec{} - NewClient(portal.Ctx) - err := opensdsClient.DeleteVolumeAttachment(id, &attachment) - - if err != nil { - reason := fmt.Sprintf("Delete attachment failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - return -} - -// GetAttachment ... -func (portal *AttachmentPortal) GetAttachment() { - id := portal.Ctx.Input.Param(":attachmentId") - NewClient(portal.Ctx) - attachment, err := opensdsClient.GetVolumeAttachment(id) - - if err != nil { - reason := fmt.Sprintf("Show attachment details failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ShowAttachmentResp(attachment) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Show attachment details, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// ListAttachmentsDetails ... -func (portal *AttachmentPortal) ListAttachmentsDetails() { - NewClient(portal.Ctx) - attachments, err := opensdsClient.ListVolumeAttachments() - if err != nil { - reason := fmt.Sprintf("List attachments with details failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ListAttachmentsDetailsResp(attachments) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("List attachments with details, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// ListAttachments ... -func (portal *AttachmentPortal) ListAttachments() { - NewClient(portal.Ctx) - attachments, err := opensdsClient.ListVolumeAttachments() - if err != nil { - reason := fmt.Sprintf("List attachments failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ListAttachmentsResp(attachments) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("List attachments, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// CreateAttachment ... -func (portal *AttachmentPortal) CreateAttachment() { - var cinderReq = converter.CreateAttachmentReqSpec{} - - if err := json.NewDecoder(portal.Ctx.Request.Body).Decode(&cinderReq); err != nil { - reason := fmt.Sprintf("Create attachment, parse request body failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - attachment := converter.CreateAttachmentReq(&cinderReq) - NewClient(portal.Ctx) - attachment, err := opensdsClient.CreateVolumeAttachment(attachment) - - if err != nil { - reason := fmt.Sprintf("Create attachment failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.CreateAttachmentResp(attachment) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Create attachment, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// UpdateAttachment ... -func (portal *AttachmentPortal) UpdateAttachment() { - id := portal.Ctx.Input.Param(":attachmentId") - var cinderReq = converter.UpdateAttachmentReqSpec{} - - if err := json.NewDecoder(portal.Ctx.Request.Body).Decode(&cinderReq); err != nil { - reason := fmt.Sprintf("Update an attachment, parse request body failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - attachment := converter.UpdateAttachmentReq(&cinderReq) - NewClient(portal.Ctx) - attachment, err := opensdsClient.UpdateVolumeAttachment(id, attachment) - - if err != nil { - reason := fmt.Sprintf("Update an attachment failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.UpdateAttachmentResp(attachment) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Update an attachment, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} diff --git a/contrib/cindercompatibleapi/api/attachment_test.go b/contrib/cindercompatibleapi/api/attachment_test.go deleted file mode 100644 index d348dfd19..000000000 --- a/contrib/cindercompatibleapi/api/attachment_test.go +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright (c) 2018 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 api - -import ( - "bytes" - "encoding/json" - "net/http" - "net/http/httptest" - "reflect" - "testing" - - "github.com/astaxie/beego" - c "github.com/opensds/opensds/client" - "github.com/opensds/opensds/contrib/cindercompatibleapi/converter" -) - -func init() { - beego.Router("/V3/attachments/:attachmentId", &AttachmentPortal{}, - "get:GetAttachment;delete:DeleteAttachment;put:UpdateAttachment") - beego.Router("/V3/attachments/detail", &AttachmentPortal{}, - "get:ListAttachmentsDetails") - beego.Router("/V3/attachments", &AttachmentPortal{}, - "post:CreateAttachment;get:ListAttachments") - - opensdsClient = c.NewFakeClient(&c.Config{Endpoint: c.TestEp}) -} - -//////////////////////////////////////////////////////////////////////////////// -// Tests for Attachment // -//////////////////////////////////////////////////////////////////////////////// -func TestGetAttachment(t *testing.T) { - r, _ := http.NewRequest("GET", "/V3/attachments/f2dda3d2-bf79-11e7-8665-f750b088f63e", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ShowAttachmentRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "attachment": { - "id": "f2dda3d2-bf79-11e7-8665-f750b088f63e", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "status": "available", - "connection_info": { - "driver_volume_type": "iscsi", - "data": { - "discard": false, - "targetDiscovered": true, - "targetIqn": "iqn.2017-10.io.opensds:volume:00000001", - "targetPortal": "127.0.0.0.1:3260" - } - } - } - }` - - var expected converter.ShowAttachmentRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestListAttachments(t *testing.T) { - r, _ := http.NewRequest("GET", "/V3/attachments", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ListAttachmentsRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "attachments": [{ - "id": "f2dda3d2-bf79-11e7-8665-f750b088f63e", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "status": "available" - }] - }` - - var expected converter.ListAttachmentsRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestListAttachmentsDetails(t *testing.T) { - r, _ := http.NewRequest("GET", "/V3/attachments/detail", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ListAttachmentsDetailsRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "attachments": [{ - "id": "f2dda3d2-bf79-11e7-8665-f750b088f63e", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "status": "available", - "connection_info": { - "driver_volume_type": "iscsi", - "data": { - "discard": false, - "targetDiscovered": true, - "targetIqn": "iqn.2017-10.io.opensds:volume:00000001", - "targetPortal": "127.0.0.0.1:3260" - } - } - }] - }` - - var expected converter.ListAttachmentsDetailsRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestCreateAttachment(t *testing.T) { - RequestBodyStr := ` - { - "attachment": { - "id": "", - "volume_uuid": "bd5b12a8-a101-11e7-941e-d77981b584d8" - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/V3/attachments", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.CreateAttachmentRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "attachment": { - "id": "f2dda3d2-bf79-11e7-8665-f750b088f63e", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "status": "available", - "connection_info": { - "driver_volume_type": "iscsi", - "data": { - "discard": false, - "targetDiscovered": true, - "targetIqn": "iqn.2017-10.io.opensds:volume:00000001", - "targetPortal": "127.0.0.0.1:3260" - } - } - } - }` - - var expected converter.CreateAttachmentRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - expected.Attachment.ID = "f2dda3d2-bf79-11e7-8665-f750b088f63e" - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } - -} - -func TestCreateAttachmentWithBadRequest(t *testing.T) { - RequestBodyStr := ` - { - "attachment": { - "id": "", - "volume_uuid": "bd5b12a8-a101-11e7-941e-d77981b584d8", - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/V3/attachments", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - var output ErrorSpec - json.Unmarshal(w.Body.Bytes(), &output) - expected := "Create attachment, parse request body failed: invalid character '}' looking for beginning of object key string" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } -} - -func TestDeleteAttachment(t *testing.T) { - r, _ := http.NewRequest("DELETE", "/V3/attachments/f2dda3d2-bf79-11e7-8665-f750b088f63e", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } -} - -func TestUpdateAttachment(t *testing.T) { - RequestBodyStr := ` - { - "attachment": { - "connector": { - "ip": "127.0.0.0.1" - } - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("PUT", "/V3/attachments/f2dda3d2-bf79-11e7-8665-f750b088f63e", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.UpdateAttachmentRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "attachment": { - "id": "f2dda3d2-bf79-11e7-8665-f750b088f63e", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "status": "available", - "connection_info": { - "driver_volume_type": "iscsi", - "data": { - "discard": false, - "targetDiscovered": true, - "targetIqn": "iqn.2017-10.io.opensds:volume:00000001", - "targetPortal": "127.0.0.0.1:3260" - } - } - } - }` - - var expected converter.UpdateAttachmentRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - expected.Attachment.ID = "f2dda3d2-bf79-11e7-8665-f750b088f63e" - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestUpdateAttachmentWithBadRequest(t *testing.T) { - RequestBodyStr := ` - { - "attachment": { - "connector": { - "ip": "127.0.0.0.1", - } - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("PUT", "/V3/attachments/f2dda3d2-bf79-11e7-8665-f750b088f63e", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - var output ErrorSpec - json.Unmarshal(w.Body.Bytes(), &output) - expected := "Update an attachment, parse request body failed: invalid character '}' looking for beginning of object key string" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } -} diff --git a/contrib/cindercompatibleapi/api/router.go b/contrib/cindercompatibleapi/api/router.go deleted file mode 100644 index 2ca51d0b1..000000000 --- a/contrib/cindercompatibleapi/api/router.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS northbound REST service. - -*/ - -package api - -import ( - "fmt" - "net/url" - "os" - "strings" - - "github.com/astaxie/beego" - bctx "github.com/astaxie/beego/context" - log "github.com/golang/glog" - c "github.com/opensds/opensds/client" - "github.com/opensds/opensds/contrib/cindercompatibleapi/converter" - "github.com/opensds/opensds/pkg/utils/constants" -) - -var ( - opensdsEndpoint string - opensdsClient *c.Client - authStrategy string -) - -// ErrorSpec describes Detailed HTTP error response, which consists of a HTTP -// status code, and a custom error message unique for each failure case. -type ErrorSpec struct { - Code int `json:"code,omitempty"` - Message string `json:"message,omitempty"` -} - -// Run ... -func Run(cinderEndpoint string) { - var ok bool - opensdsEndpointFromEnv, ok := os.LookupEnv(c.OpensdsEndpoint) - if !ok { - fmt.Println("ERROR: You must provide the endpoint by setting " + - "the environment variable " + c.OpensdsEndpoint) - return - } - - if "" == opensdsEndpointFromEnv { - opensdsEndpoint = constants.DefaultOpensdsEndpoint - fmt.Println("Warnning: OpenSDS Endpoint is not specified using the default value:" + opensdsEndpoint) - } else { - opensdsEndpoint = opensdsEndpointFromEnv - } - - // cinderEndpoint: http://127.0.0.1:8777/v3 - words := strings.Split(cinderEndpoint, "/") - if (len(words) < 4) || (words[3] != converter.APIVersion) { - fmt.Println("The environment variable CINDER_ENDPOINT is set incorrectly") - return - } - - authStrategy, ok = os.LookupEnv(c.OpensdsAuthStrategy) - if !ok { - authStrategy = c.Noauth - fmt.Println("WARNING: Not found Env " + c.OpensdsAuthStrategy + ", use default(noauth)") - } - - log.Info("authStrategy: " + authStrategy) - cfg := &c.Config{Endpoint: opensdsEndpoint} - switch authStrategy { - case c.Keystone: - // opensdsClient is generated by NewClient later - break - case c.Noauth: - cfg.AuthOptions = c.LoadNoAuthOptionsFromEnv() - opensdsClient = c.NewClient(cfg) - default: - cfg.AuthOptions = c.NewNoauthOptions(constants.DefaultTenantId) - opensdsClient = c.NewClient(cfg) - } - - ns := - beego.NewNamespace("/"+converter.APIVersion, - beego.NSCond(func(ctx *bctx.Context) bool { - // To judge whether the scheme is legal or not. - if ctx.Input.Scheme() != "http" && ctx.Input.Scheme() != "https" { - return false - } - return true - }), - beego.NSNamespace("/:projectId", - beego.NSRouter("/types", &TypePortal{}, "post:CreateType;get:ListTypes"), - beego.NSRouter("/types/:volumeTypeId", &TypePortal{}, "get:GetType;put:UpdateType;delete:DeleteType"), - beego.NSRouter("/types/:volumeTypeId/extra_specs", &TypePortal{}, "post:AddExtraProperty;get:ListExtraProperties"), - beego.NSRouter("/types/:volumeTypeId/extra_specs/:key", &TypePortal{}, "get:ShowExtraProperty;put:UpdateExtraProperty;delete:DeleteExtraProperty"), - - beego.NSRouter("/volumes", &VolumePortal{}, "post:CreateVolume;get:ListVolumes"), - beego.NSRouter("/volumes/detail", &VolumePortal{}, "get:ListVolumesDetails"), - beego.NSRouter("/volumes/:volumeId", &VolumePortal{}, "get:GetVolume;delete:DeleteVolume;put:UpdateVolume"), - beego.NSRouter("/volumes/:volumeId/action", &VolumePortal{}, "post:VolumeAction"), - - beego.NSRouter("/attachments", &AttachmentPortal{}, "post:CreateAttachment;get:ListAttachments"), - beego.NSRouter("/attachments/detail", &AttachmentPortal{}, "get:ListAttachmentsDetails"), - beego.NSRouter("/attachments/:attachmentId", &AttachmentPortal{}, "get:GetAttachment;delete:DeleteAttachment;put:UpdateAttachment"), - - beego.NSRouter("/snapshots", &SnapshotPortal{}, "post:CreateSnapshot;get:ListSnapshots"), - beego.NSRouter("/snapshots/detail", &SnapshotPortal{}, "get:ListSnapshotsDetails"), - beego.NSRouter("/snapshots/:snapshotId", &SnapshotPortal{}, "get:GetSnapshot;delete:DeleteSnapshot;put:UpdateSnapshot"), - ), - ) - - beego.AddNamespace(ns) - beego.Router("/", &VersionPortal{}, "get:ListAllAPIVersions") - - // start service - beego.Run(words[2]) -} - -// NewClient Recreate the client only when authStrategy == c.Keystone -// and copy it to the global variable opensdsClient -func NewClient(ctx *bctx.Context) { - if authStrategy == c.Keystone { - reqURL := strings.TrimSpace(ctx.Request.URL.String()) - - // When "List Api Versions", the URL has no project_id, - // so no authentication - if "/" == reqURL { - cfg := &c.Config{Endpoint: opensdsEndpoint} - cfg.AuthOptions = c.LoadNoAuthOptionsFromEnv() - opensdsClient = c.NewClient(cfg) - } else { - tenantId := GetProjectId(reqURL) - tokenID := ctx.Input.Header(constants.AuthTokenHeader) - log.V(5).Info("TenantId:" + tenantId + ", " + "TokenID:" + tokenID + "!!!") - - r := &c.KeystoneReciver{Auth: &c.KeystoneAuthOptions{TenantID: tokenID, - TokenID: tokenID}} - - opensdsClient = &c.Client{ - ProfileMgr: c.NewProfileMgr(r, opensdsEndpoint, tenantId), - DockMgr: c.NewDockMgr(r, opensdsEndpoint, tenantId), - PoolMgr: c.NewPoolMgr(r, opensdsEndpoint, tenantId), - VolumeMgr: c.NewVolumeMgr(r, opensdsEndpoint, tenantId), - VersionMgr: c.NewVersionMgr(r, opensdsEndpoint, tenantId), - ReplicationMgr: c.NewReplicationMgr(r, opensdsEndpoint, tenantId), - } - } - } -} - -// GetProjectId Get the value of project_id -func GetProjectId(reqURL string) string { - u, err := url.Parse(reqURL) - if err != nil { - log.Error("url.Parse failed:" + err.Error()) - return "" - } - - // /v3/{project_id}/ - words := strings.Split(u.Path, "/") - - if (len(words) >= 3) && (len(words[2]) > 0) { - log.V(5).Info("project_id is" + words[2]) - return words[2] - } else { - log.Error("there is no project_id in the request URL:" + reqURL) - return "" - } -} diff --git a/contrib/cindercompatibleapi/api/snapshot.go b/contrib/cindercompatibleapi/api/snapshot.go deleted file mode 100644 index 8bec567bb..000000000 --- a/contrib/cindercompatibleapi/api/snapshot.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS northbound service. - -*/ - -package api - -import ( - "encoding/json" - "fmt" - "net/http" - - "github.com/astaxie/beego" - log "github.com/golang/glog" - "github.com/opensds/opensds/contrib/cindercompatibleapi/converter" - "github.com/opensds/opensds/pkg/model" -) - -// SnapshotPortal ... -type SnapshotPortal struct { - beego.Controller -} - -// ListSnapshotsDetails ... -func (portal *SnapshotPortal) ListSnapshotsDetails() { - NewClient(portal.Ctx) - snapshots, err := opensdsClient.ListVolumeSnapshots() - if err != nil { - reason := fmt.Sprintf("List snapshots and details failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ListSnapshotsDetailsResp(snapshots) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("List snapshots and details, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// CreateSnapshot ... -func (portal *SnapshotPortal) CreateSnapshot() { - var cinderReq = converter.CreateSnapshotReqSpec{} - if err := json.NewDecoder(portal.Ctx.Request.Body).Decode(&cinderReq); err != nil { - reason := fmt.Sprintf("Create a snapshot, parse request body failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - snapshot, err := converter.CreateSnapshotReq(&cinderReq) - if err != nil { - reason := fmt.Sprintf("Create a snapshot failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - NewClient(portal.Ctx) - snapshot, err = opensdsClient.CreateVolumeSnapshot(snapshot) - if err != nil { - reason := fmt.Sprintf("Create a snapshot failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.CreateSnapshotResp(snapshot) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Create a snapshot, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusAccepted) - portal.Ctx.Output.Body(body) - return -} - -// ListSnapshots ... -func (portal *SnapshotPortal) ListSnapshots() { - NewClient(portal.Ctx) - snapshots, err := opensdsClient.ListVolumeSnapshots() - if err != nil { - reason := fmt.Sprintf("List accessible snapshots failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ListSnapshotsResp(snapshots) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("List accessible snapshots, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// GetSnapshot ... -func (portal *SnapshotPortal) GetSnapshot() { - id := portal.Ctx.Input.Param(":snapshotId") - NewClient(portal.Ctx) - snapshot, err := opensdsClient.GetVolumeSnapshot(id) - - if err != nil { - reason := fmt.Sprintf("Show a snapshot's details failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ShowSnapshotDetailsResp(snapshot) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Show a snapshot's details, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// UpdateSnapshot ... -func (portal *SnapshotPortal) UpdateSnapshot() { - id := portal.Ctx.Input.Param(":snapshotId") - var cinderUpdateReq = converter.UpdateSnapshotReqSpec{} - - if err := json.NewDecoder(portal.Ctx.Request.Body).Decode(&cinderUpdateReq); err != nil { - reason := fmt.Sprintf("Update a snapshot, parse request body failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - snapshot := converter.UpdateSnapshotReq(&cinderUpdateReq) - NewClient(portal.Ctx) - snapshot, err := opensdsClient.UpdateVolumeSnapshot(id, snapshot) - - if err != nil { - reason := fmt.Sprintf("Update a snapshot failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.UpdateSnapshotResp(snapshot) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Update a snapshot, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// DeleteSnapshot ... -func (portal *SnapshotPortal) DeleteSnapshot() { - id := portal.Ctx.Input.Param(":snapshotId") - NewClient(portal.Ctx) - err := opensdsClient.DeleteVolumeSnapshot(id, nil) - - if err != nil { - reason := fmt.Sprintf("Delete a snapshot failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusAccepted) - return -} diff --git a/contrib/cindercompatibleapi/api/snapshot_test.go b/contrib/cindercompatibleapi/api/snapshot_test.go deleted file mode 100644 index 693b7d366..000000000 --- a/contrib/cindercompatibleapi/api/snapshot_test.go +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright (c) 2018 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 api - -import ( - "bytes" - "encoding/json" - "net/http" - "net/http/httptest" - "reflect" - "testing" - - "github.com/astaxie/beego" - c "github.com/opensds/opensds/client" - "github.com/opensds/opensds/contrib/cindercompatibleapi/converter" -) - -func init() { - beego.Router("/V3/snapshots/:snapshotId", &SnapshotPortal{}, - "get:GetSnapshot;delete:DeleteSnapshot;put:UpdateSnapshot") - beego.Router("/V3/snapshots", &SnapshotPortal{}, - "post:CreateSnapshot;get:ListSnapshots") - beego.Router("/V3/snapshots/detail", &SnapshotPortal{}, - "get:ListSnapshotsDetails") - - opensdsClient = c.NewFakeClient(&c.Config{Endpoint: c.TestEp}) -} - -//////////////////////////////////////////////////////////////////////////////// -// Tests for Snapshot // -//////////////////////////////////////////////////////////////////////////////// -func TestCreateSnapshot(t *testing.T) { - RequestBodyStr := ` - { - "snapshot": { - "name": "sample-snapshot-01", - "description": "This is the first sample snapshot for testing", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "metadata": null - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/V3/snapshots", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.CreateSnapshotRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - var expected converter.CreateSnapshotRespSpec - json.Unmarshal([]byte(RequestBodyStr), &expected) - - if w.Code != http.StatusAccepted { - t.Errorf("Expected %v, actual %v", http.StatusAccepted, w.Code) - } - - expected.Snapshot.ID = "3769855c-a102-11e7-b772-17b880d2f537" - expected.Snapshot.VolumeID = "bd5b12a8-a101-11e7-941e-d77981b584d8" - expected.Snapshot.Status = "available" - expected.Snapshot.Size = 1 - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestCreateSnapshotWithBadRequest(t *testing.T) { - RequestBodyStr := ` - { - "snapshot": { - "name": "sample-snapshot-01", - "description": "This is the first sample snapshot for testing", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "metadata": null, - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/V3/snapshots", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - var output ErrorSpec - json.Unmarshal(w.Body.Bytes(), &output) - expected := "Create a snapshot, parse request body failed: invalid character '}' looking for beginning of object key string" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } - - RequestBodyStr = ` - { - "snapshot": { - "name": "sample-snapshot-01", - "description": "This is the first sample snapshot for testing", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "force": true - } - }` - - jsonStr = []byte(RequestBodyStr) - r, _ = http.NewRequest("POST", "/V3/snapshots", bytes.NewBuffer(jsonStr)) - w = httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - json.Unmarshal(w.Body.Bytes(), &output) - expected = "Create a snapshot failed: OpenSDS does not support the parameter: force" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } - - RequestBodyStr = ` - { - "snapshot": { - "name": "sample-snapshot-01", - "description": "This is the first sample snapshot for testing", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "metadata": { - "key1": "value1" - } - } - }` - - jsonStr = []byte(RequestBodyStr) - r, _ = http.NewRequest("POST", "/V3/snapshots", bytes.NewBuffer(jsonStr)) - w = httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - json.Unmarshal(w.Body.Bytes(), &output) - expected = "Create a snapshot failed: OpenSDS does not support the parameter: metadata" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } -} - -func TestGetSnapshot(t *testing.T) { - r, _ := http.NewRequest("GET", "/V3/snapshots/f2dda3d2-bf79-11e7-8665-f750b088f63e", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ShowSnapshotDetailsRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "snapshot": { - "status": "available", - "size": 1, - "id": "3769855c-a102-11e7-b772-17b880d2f537", - "name": "sample-snapshot-01", - "description": "This is the first sample snapshot for testing", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8" - } - }` - - var expected converter.ShowSnapshotDetailsRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestListSnapshots(t *testing.T) { - r, _ := http.NewRequest("GET", "/V3/snapshots", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output []converter.ListSnapshotsRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "snapshots": [{ - "status": "created", - "description": "This is the first sample snapshot for testing", - "name": "sample-snapshot-01", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "id": "3769855c-a102-11e7-b772-17b880d2f537", - "size": 1 - }, - { - "status": "created", - "description": "This is the second sample snapshot for testing", - "name": "sample-snapshot-02", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "id": "3bfaf2cc-a102-11e7-8ecb-63aea739d755", - "size": 1 - }] - }` - - var expected []converter.ListSnapshotsRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestListSnapshotsDetails(t *testing.T) { - r, _ := http.NewRequest("GET", "/V3/snapshots/detail", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output []converter.ListSnapshotsDetailsRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "snapshots": [{ - "status": "created", - "description": "This is the first sample snapshot for testing", - "name": "sample-snapshot-01", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "id": "3769855c-a102-11e7-b772-17b880d2f537", - "size": 1 - }, - { - "status": "created", - "description": "This is the second sample snapshot for testing", - "name": "sample-snapshot-02", - "volume_id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "id": "3bfaf2cc-a102-11e7-8ecb-63aea739d755", - "size": 1 - }] - }` - - var expected []converter.ListSnapshotsDetailsRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestDeleteSnapshot(t *testing.T) { - r, _ := http.NewRequest("DELETE", "/V3/snapshots/3769855c-a102-11e7-b772-17b880d2f537", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusAccepted { - t.Errorf("Expected %v, actual %v", http.StatusAccepted, w.Code) - } -} - -func TestUpdateSnapshot(t *testing.T) { - RequestBodyStr := ` - { - "snapshot": { - "name": "sample-snapshot-01", - "description": "This is the first sample snapshot for testing" - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("PUT", "/V3/snapshots/3769855c-a102-11e7-b772-17b880d2f537", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.UpdateSnapshotRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - var expected converter.UpdateSnapshotRespSpec - json.Unmarshal([]byte(RequestBodyStr), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - expected.Snapshot.ID = "3769855c-a102-11e7-b772-17b880d2f537" - expected.Snapshot.VolumeID = "bd5b12a8-a101-11e7-941e-d77981b584d8" - expected.Snapshot.Status = "available" - expected.Snapshot.Size = 1 - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestUpdateSnapshotWithBadRequest(t *testing.T) { - RequestBodyStr := ` - { - "snapshot": { - "name": "sample-snapshot-01", - "description": "This is the first sample snapshot for testing", - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("PUT", "/V3/snapshots/3769855c-a102-11e7-b772-17b880d2f537", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - var output ErrorSpec - json.Unmarshal(w.Body.Bytes(), &output) - expected := "Update a snapshot, parse request body failed: invalid character '}' looking for beginning of object key string" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } -} diff --git a/contrib/cindercompatibleapi/api/volume.go b/contrib/cindercompatibleapi/api/volume.go deleted file mode 100644 index cfffa2888..000000000 --- a/contrib/cindercompatibleapi/api/volume.go +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS northbound service. - -*/ - -package api - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "strings" - "time" - - "github.com/astaxie/beego" - log "github.com/golang/glog" - "github.com/opensds/opensds/contrib/cindercompatibleapi/converter" - "github.com/opensds/opensds/pkg/model" -) - -// VolumePortal ... -type VolumePortal struct { - beego.Controller -} - -var ( - // SleepDuration When running unit tests, it should be set to time.Nanosecond - SleepDuration = time.Second -) - -// ListVolumesDetails ... -func (portal *VolumePortal) ListVolumesDetails() { - NewClient(portal.Ctx) - volumes, err := opensdsClient.ListVolumes() - if err != nil { - reason := fmt.Sprintf("List accessible volumes with details failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ListVolumesDetailsResp(volumes) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("List accessible volumes with details, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// CreateVolume ... -func (portal *VolumePortal) CreateVolume() { - var cinderReq = converter.CreateVolumeReqSpec{} - - if err := json.NewDecoder(portal.Ctx.Request.Body).Decode(&cinderReq); err != nil { - reason := fmt.Sprintf("Create a volume, parse request body failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - volume, err := converter.CreateVolumeReq(&cinderReq) - if err != nil { - reason := fmt.Sprintf("Create a volume failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - NewClient(portal.Ctx) - volume, err = opensdsClient.CreateVolume(volume) - if err != nil { - reason := fmt.Sprintf("Create a volume failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.CreateVolumeResp(volume) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Create a volume, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusAccepted) - portal.Ctx.Output.Body(body) - return -} - -// ListVolumes ... -func (portal *VolumePortal) ListVolumes() { - NewClient(portal.Ctx) - volumes, err := opensdsClient.ListVolumes() - if err != nil { - reason := fmt.Sprintf("List accessible volumes failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ListVolumesResp(volumes) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("List accessible volumes, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// GetVolume ... -func (portal *VolumePortal) GetVolume() { - id := portal.Ctx.Input.Param(":volumeId") - NewClient(portal.Ctx) - volume, err := opensdsClient.GetVolume(id) - - if err != nil { - reason := fmt.Sprintf("Show a volume's details failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ShowVolumeResp(volume) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Show a volume's details, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// UpdateVolume ... -func (portal *VolumePortal) UpdateVolume() { - id := portal.Ctx.Input.Param(":volumeId") - var cinderReq = converter.UpdateVolumeReqSpec{} - - if err := json.NewDecoder(portal.Ctx.Request.Body).Decode(&cinderReq); err != nil { - reason := fmt.Sprintf("Update a volume, parse request body failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - volume, err := converter.UpdateVolumeReq(&cinderReq) - if err != nil { - reason := fmt.Sprintf("Update a volume failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - NewClient(portal.Ctx) - volume, err = opensdsClient.UpdateVolume(id, volume) - - if err != nil { - reason := fmt.Sprintf("Update a volume failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.UpdateVolumeResp(volume) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Update a volume, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// DeleteVolume ... -func (portal *VolumePortal) DeleteVolume() { - id := portal.Ctx.Input.Param(":volumeId") - volume := model.VolumeSpec{} - NewClient(portal.Ctx) - err := opensdsClient.DeleteVolume(id, &volume) - - if err != nil { - reason := fmt.Sprintf("Delete a volume failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusAccepted) - return -} - -// VolumeAction ... -func (portal *VolumePortal) VolumeAction() { - id := portal.Ctx.Input.Param(":volumeId") - byts, err := ioutil.ReadAll(portal.Ctx.Request.Body) - if err != nil { - reason := fmt.Sprintf("Volume actions failed: request body is incorrect") - log.Error(reason) - portal.Ctx.Output.SetStatus(http.StatusNotFound) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - return - } - - rawBodyText := string(byts) - - // No actual operation is currently done - if `{"os-reserve": null}` == rawBodyText { - portal.Ctx.Output.SetStatus(http.StatusAccepted) - return - } - - if strings.HasPrefix(rawBodyText, `{"os-initialize_connection"`) { - var cinderReq = converter.InitializeConnectionReqSpec{} - err = json.Unmarshal([]byte(rawBodyText), &cinderReq) - - if err != nil { - reason := fmt.Sprintf("Initialize connection, parse request body failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - attachment := converter.InitializeConnectionReq(&cinderReq, id) - NewClient(portal.Ctx) - attachment, err := opensdsClient.CreateVolumeAttachment(attachment) - - if err != nil { - reason := fmt.Sprintf("Initialize connection failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - isAvailable := false - sum := 0 - - for { - sum++ - time.Sleep(SleepDuration) - attachment, _ = opensdsClient.GetVolumeAttachment(attachment.Id) - if ("available" == attachment.Status) && ("" != attachment.ConnectionInfo.DriverVolumeType) && - //(nil != attachment.ConnectionInfo.ConnectionData["authPassword"]) && - (nil != attachment.ConnectionInfo.ConnectionData["targetDiscovered"]) && - //(nil != attachment.ConnectionInfo.ConnectionData["encrypted"]) && - (nil != attachment.ConnectionInfo.ConnectionData["targetIQN"]) && - (nil != attachment.ConnectionInfo.ConnectionData["targetPortal"]) && - //(nil != attachment.ConnectionInfo.ConnectionData["volumeId"]) && - (nil != attachment.ConnectionInfo.ConnectionData["targetLun"]) { - //(nil != attachment.ConnectionInfo.ConnectionData["accessMode"]) && - //(nil != attachment.ConnectionInfo.ConnectionData["authUserName"]) && - //(nil != attachment.ConnectionInfo.ConnectionData["authMethod"]) { - isAvailable = true - break - } - - //The maximum waiting time is 10 seconds - if 10 == sum { - break - } - } - - if false == isAvailable { - reason := fmt.Sprintf("Initialize connection, attachment is not available or connectionInfo is incorrect") - attachmentByts, _ := json.Marshal(attachment) - fmt.Println(string(attachmentByts)) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.InitializeConnectionResp(attachment) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Initialize connection, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return - } - - if strings.HasPrefix(rawBodyText, `{"os-terminate_connection":`) { - portal.Ctx.Output.SetStatus(http.StatusAccepted) - return - } - - if `{"os-unreserve": null}` == rawBodyText { - portal.Ctx.Output.SetStatus(http.StatusAccepted) - return - } - - if strings.HasPrefix(rawBodyText, `{"os-attach":`) { - portal.Ctx.Output.SetStatus(http.StatusAccepted) - return - } - - if strings.HasPrefix(rawBodyText, `{"os-detach":`) { - portal.Ctx.Output.SetStatus(http.StatusAccepted) - return - } - - if strings.HasPrefix(rawBodyText, `{"os-begin_detaching":`) { - portal.Ctx.Output.SetStatus(http.StatusAccepted) - return - } - - reason := fmt.Sprintf("Volume actions failed: the body of the request is wrong or not currently supported") - log.Error("Volume actions failed: " + rawBodyText + " is incorrect") - portal.Ctx.Output.SetStatus(http.StatusNotFound) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - return -} diff --git a/contrib/cindercompatibleapi/api/volume_test.go b/contrib/cindercompatibleapi/api/volume_test.go deleted file mode 100644 index 6d313c0f6..000000000 --- a/contrib/cindercompatibleapi/api/volume_test.go +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright (c) 2018 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 api - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "reflect" - "testing" - "time" - - "github.com/astaxie/beego" - c "github.com/opensds/opensds/client" - "github.com/opensds/opensds/contrib/cindercompatibleapi/converter" -) - -func init() { - beego.Router("/v3/volumes/:volumeId/action", &VolumePortal{}, - "post:VolumeAction") - beego.Router("/v3/volumes/:volumeId", &VolumePortal{}, - "get:GetVolume;delete:DeleteVolume;put:UpdateVolume") - beego.Router("/v3/volumes/detail", &VolumePortal{}, - "get:ListVolumesDetails") - beego.Router("/v3/volumes", &VolumePortal{}, - "post:CreateVolume;get:ListVolumes") - - opensdsClient = c.NewFakeClient(&c.Config{Endpoint: c.TestEp}) -} - -//////////////////////////////////////////////////////////////////////////////// -// Tests for Volume // -//////////////////////////////////////////////////////////////////////////////// -func TestGetVolume(t *testing.T) { - r, _ := http.NewRequest("GET", "/v3/volumes/bd5b12a8-a101-11e7-941e-d77981b584d8", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ShowVolumeRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "volume": { - "id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "name": "sample-volume", - "description": "This is a sample volume for testing", - "metadata": { - - }, - "size": 1 - } - }` - - var expected converter.ShowVolumeRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - expected.Volume.Attachments = make([]converter.RespAttachment, 0, 0) - expected.Volume.Status = "available" - expected.Volume.VolumeType = "1106b972-66ef-11e7-b172-db03f3689c9c" - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestListVolumes(t *testing.T) { - r, _ := http.NewRequest("GET", "/v3/volumes", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ListVolumesRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "volumes": [{ - "id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "metadata": { - - }, - "name": "sample-volume" - }] - }` - - var expected converter.ListVolumesRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestListVolumesDetails(t *testing.T) { - r, _ := http.NewRequest("GET", "/v3/volumes/detail", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ListVolumesDetailsRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "volumes": [{ - "id": "bd5b12a8-a101-11e7-941e-d77981b584d8", - "size": 1, - "status": "available", - "description": "This is a sample volume for testing", - "metadata": { - - }, - "volume_type": "1106b972-66ef-11e7-b172-db03f3689c9c", - "name": "sample-volume" - }] - }` - - var expected converter.ListVolumesDetailsRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - expected.Volumes[0].Attachments = make([]converter.RespAttachment, 0, 0) - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestCreateVolume(t *testing.T) { - RequestBodyStr := ` - { - "volume": { - "name": "sample-volume", - "description": "This is a sample volume for testing", - "size": 1 - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/v3/volumes", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.CreateVolumeRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - var expected converter.CreateVolumeRespSpec - json.Unmarshal([]byte(RequestBodyStr), &expected) - - if w.Code != http.StatusAccepted { - t.Errorf("Expected %v, actual %v", http.StatusAccepted, w.Code) - } - - expected.Volume.Attachments = make([]converter.RespAttachment, 0, 0) - expected.Volume.Status = "available" - expected.Volume.ID = "bd5b12a8-a101-11e7-941e-d77981b584d8" - expected.Volume.Metadata = make(map[string]string) - expected.Volume.VolumeType = "1106b972-66ef-11e7-b172-db03f3689c9c" - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestCreateVolumeWithBadRequest(t *testing.T) { - RequestBodyStr := ` - { - "volume": { - "name": "sample-volume", - "description": "This is a sample volume for testing", - "size": 1, - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/v3/volumes", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - var output ErrorSpec - json.Unmarshal(w.Body.Bytes(), &output) - expected := "Create a volume, parse request body failed: invalid character '}' looking for beginning of object key string" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } - - RequestBodyStr = ` - { - "volume": { - "name": "sample-volume", - "description": "This is a sample volume for testing", - "size": 1, - "multiattach": true - } - }` - - jsonStr = []byte(RequestBodyStr) - r, _ = http.NewRequest("POST", "/v3/volumes", bytes.NewBuffer(jsonStr)) - w = httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - json.Unmarshal(w.Body.Bytes(), &output) - expected = "Create a volume failed: OpenSDS does not support the parameter: id/source_volid/multiattach/snapshot_id/backup_id/imageRef/metadata/consistencygroup_id" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } - -} - -func TestDeleteVolume(t *testing.T) { - r, _ := http.NewRequest("DELETE", "/v3/volumes/bd5b12a8-a101-11e7-941e-d77981b584d8", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusAccepted { - t.Errorf("Expected %v, actual %v", http.StatusAccepted, w.Code) - } -} - -func TestUpdateVolume(t *testing.T) { - RequestBodyStr := ` - { - "volume": { - "name": "sample-volume", - "multiattach": false, - "description": "This is a sample volume for testing" - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("PUT", "/v3/volumes/bd5b12a8-a101-11e7-941e-d77981b584d8", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.UpdateVolumeRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - var expected converter.UpdateVolumeRespSpec - json.Unmarshal([]byte(RequestBodyStr), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - expected.Volume.Attachments = make([]converter.RespAttachment, 0, 0) - expected.Volume.Status = "available" - expected.Volume.ID = "bd5b12a8-a101-11e7-941e-d77981b584d8" - expected.Volume.Size = 1 - expected.Volume.Metadata = make(map[string]string) - expected.Volume.VolumeType = "1106b972-66ef-11e7-b172-db03f3689c9c" - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestUpdateVolumeWithBadRequest(t *testing.T) { - RequestBodyStr := ` - { - "volume": { - "name": "sample-volume", - "multiattach": false, - "description": "This is a sample volume for testing", - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("PUT", "/v3/volumes/bd5b12a8-a101-11e7-941e-d77981b584d8", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - var output ErrorSpec - json.Unmarshal(w.Body.Bytes(), &output) - expected := "Update a volume, parse request body failed: invalid character '}' looking for beginning of object key string" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } - - RequestBodyStr = ` - { - "volume": { - "name": "sample-volume", - "multiattach": false, - "description": "This is a sample volume for testing", - "metadata": { - "key1": "value1" - } - } - }` - - jsonStr = []byte(RequestBodyStr) - r, _ = http.NewRequest("PUT", "/v3/volumes/bd5b12a8-a101-11e7-941e-d77981b584d8", bytes.NewBuffer(jsonStr)) - - w = httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - json.Unmarshal(w.Body.Bytes(), &output) - expected = "Update a volume failed: OpenSDS does not support the parameter: metadata" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } -} - -func TestVolumeAction(t *testing.T) { - RequestBodyStr := `{"os-reserve": null}` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/v3/volumes/bd5b12a8-a101-11e7-941e-d77981b584d8/action", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusAccepted { - t.Errorf("Expected %v, actual %v", http.StatusAccepted, w.Code) - } - - RequestBodyStr = ` - { - "os-extend": { - "new_size": 3 - } - }` - - jsonStr = []byte(RequestBodyStr) - r, _ = http.NewRequest("POST", "/v3/volumes/bd5b12a8-a101-11e7-941e-d77981b584d8/action", bytes.NewBuffer(jsonStr)) - w = httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusNotFound { - t.Errorf("Expected %v, actual %v", http.StatusNotFound, w.Code) - } -} - -func TestVolumeActionInitializeConnectionWithError(t *testing.T) { - SleepDuration = time.Nanosecond - Req := converter.InitializeConnectionReqSpec{} - - Req.InitializeConnection.Connector.Platform = "x86_64" - Req.InitializeConnection.Connector.Host = "ubuntu" - Req.InitializeConnection.Connector.DoLocalAttach = false - Req.InitializeConnection.Connector.IP = "10.10.3.173" - Req.InitializeConnection.Connector.OsType = "linux2" - Req.InitializeConnection.Connector.Multipath = false - Req.InitializeConnection.Connector.Initiator = "iqn.1993-08.org.debian:01:6acaf7eab14" - body, _ := json.Marshal(Req) - - RequestBodyStr := string(body) - fmt.Println("397") - fmt.Println(string(body)) - fmt.Println("399") - fmt.Println(RequestBodyStr) - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/v3/volumes/bd5b12a8-a101-11e7-941e-d77981b584d8/action", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusInternalServerError { - t.Errorf("Expected %v, actual %v", http.StatusInternalServerError, w.Code) - } - - var output ErrorSpec - json.Unmarshal(w.Body.Bytes(), &output) - expected := "Initialize connection, attachment is not available or connectionInfo is incorrect" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } -} diff --git a/contrib/cindercompatibleapi/api/volumetype.go b/contrib/cindercompatibleapi/api/volumetype.go deleted file mode 100644 index 7266dc2ce..000000000 --- a/contrib/cindercompatibleapi/api/volumetype.go +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS northbound service. - -*/ - -package api - -import ( - "encoding/json" - "fmt" - "net/http" - "os" - - "github.com/astaxie/beego" - log "github.com/golang/glog" - "github.com/opensds/opensds/contrib/cindercompatibleapi/converter" - "github.com/opensds/opensds/pkg/model" -) - -// TypePortal ... -type TypePortal struct { - beego.Controller -} - -// DefaultTypeName ... -var DefaultTypeName = "default" - -// UpdateType ... -func (portal *TypePortal) UpdateType() { - id := portal.Ctx.Input.Param(":volumeTypeId") - var cinderReq = converter.UpdateTypeReqSpec{} - if err := json.NewDecoder(portal.Ctx.Request.Body).Decode(&cinderReq); err != nil { - reason := fmt.Sprintf("Update a volume type, parse request body failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - profile, err := converter.UpdateTypeReq(&cinderReq) - if err != nil { - reason := fmt.Sprintf("Update a volume type failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - NewClient(portal.Ctx) - profile, err = opensdsClient.UpdateProfile(id, profile) - if err != nil { - reason := fmt.Sprintf("Update a volume type failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.UpdateTypeResp(profile) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Update a volume type, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// AddExtraProperty ... -func (portal *TypePortal) AddExtraProperty() { - id := portal.Ctx.Input.Param(":volumeTypeId") - var cinderReq = converter.AddExtraReqSpec{} - if err := json.NewDecoder(portal.Ctx.Request.Body).Decode(&cinderReq); err != nil { - reason := fmt.Sprintf("Create or update extra specs for volume type, parse request body failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - profileExtra := converter.AddExtraReq(&cinderReq) - NewClient(portal.Ctx) - profileExtra, err := opensdsClient.AddCustomProperty(id, profileExtra) - if err != nil { - reason := fmt.Sprintf("Create or update extra specs for volume type failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.AddExtraResp(profileExtra) - // Marshal the result. - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Create or update extra specs for volume type, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// ListExtraProperties ... -func (portal *TypePortal) ListExtraProperties() { - id := portal.Ctx.Input.Param(":volumeTypeId") - NewClient(portal.Ctx) - profileExtra, err := opensdsClient.ListCustomProperties(id) - - if err != nil { - reason := fmt.Sprintf("Show all extra specifications for volume type failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ShowAllExtraResp(profileExtra) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Show all extra specifications for volume type, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// ShowExtraProperty ... -func (portal *TypePortal) ShowExtraProperty() { - id := portal.Ctx.Input.Param(":volumeTypeId") - NewClient(portal.Ctx) - profileExtra, err := opensdsClient.ListCustomProperties(id) - - if err != nil { - reason := fmt.Sprintf("Show extra specification for volume type failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - key := portal.Ctx.Input.Param(":key") - result := converter.ShowExtraResp(key, profileExtra) - if nil == (*result) { - reason := "The key name of the extra spec for the volume type can not be found" - portal.Ctx.Output.SetStatus(http.StatusNotFound) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - body, err := json.Marshal(result) - - if err != nil { - reason := fmt.Sprintf("Show extra specification for volume type, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// UpdateExtraProperty ... -func (portal *TypePortal) UpdateExtraProperty() { - id := portal.Ctx.Input.Param(":volumeTypeId") - key := portal.Ctx.Input.Param(":key") - var cinderReq = converter.UpdateExtraReqSpec{} - - if err := json.NewDecoder(portal.Ctx.Request.Body).Decode(&cinderReq); err != nil { - reason := fmt.Sprintf("Update extra specification for volume type, parse request body failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - profileExtra, err := converter.UpdateExtraReq(key, &cinderReq) - if err != nil { - reason := fmt.Sprintf("Update extra specification for volume type failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - NewClient(portal.Ctx) - profileExtra, err = opensdsClient.AddCustomProperty(id, profileExtra) - if err != nil { - reason := fmt.Sprintf("Update extra specification for volume type failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.UpdateExtraResp(key, profileExtra) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Update extra specification for volume type, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return - -} - -// DeleteExtraProperty ... -func (portal *TypePortal) DeleteExtraProperty() { - id := portal.Ctx.Input.Param(":volumeTypeId") - key := portal.Ctx.Input.Param(":key") - NewClient(portal.Ctx) - err := opensdsClient.RemoveCustomProperty(id, key) - - if err != nil { - reason := fmt.Sprintf("Delete extra specification for volume type failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusAccepted) - return -} - -// GetType ... -func (portal *TypePortal) GetType() { - id := portal.Ctx.Input.Param(":volumeTypeId") - DefaultName := os.Getenv("DEFAULT_VOLUME_TYPE_NAME") - if ("" != DefaultName) && (DefaultTypeName != DefaultName) { - DefaultTypeName = DefaultName - log.Info("DefaultTypeName = " + DefaultTypeName) - } - - var profile *model.ProfileSpec - NewClient(portal.Ctx) - - if "default" != id { - foundProfile, err := opensdsClient.GetProfile(id) - - if err != nil { - reason := fmt.Sprintf("Get profile failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - profile = foundProfile - } else { - profiles, err := opensdsClient.ListProfiles() - if err != nil { - reason := fmt.Sprintf("List profiles failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - for _, v := range profiles { - if DefaultTypeName == v.Name { - profile = v - } - } - - if nil == profile { - reason := "Default volume type can not be found" - portal.Ctx.Output.SetStatus(http.StatusNotFound) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - } - - result := converter.ShowTypeResp(profile) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Show volume type detail, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// DeleteType ... -func (portal *TypePortal) DeleteType() { - id := portal.Ctx.Input.Param(":volumeTypeId") - NewClient(portal.Ctx) - err := opensdsClient.DeleteProfile(id) - - if err != nil { - reason := fmt.Sprintf("Delete a volume type failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusAccepted) - return -} - -// ListTypes ... -func (portal *TypePortal) ListTypes() { - NewClient(portal.Ctx) - profiles, err := opensdsClient.ListProfiles() - if err != nil { - reason := fmt.Sprintf("List all volume types failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.ListTypesResp(profiles) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("List all volume types, marshal result failed: %v", err) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} - -// CreateType ... -func (portal *TypePortal) CreateType() { - var cinderReq = converter.CreateTypeReqSpec{} - if err := json.NewDecoder(portal.Ctx.Request.Body).Decode(&cinderReq); err != nil { - reason := fmt.Sprintf("Create a volume type, parse request body failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - profile, err := converter.CreateTypeReq(&cinderReq) - if err != nil { - reason := fmt.Sprintf("Create a volume type failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorBadRequest) - portal.Ctx.Output.Body(model.ErrorBadRequestStatus(reason)) - log.Error(reason) - return - } - - NewClient(portal.Ctx) - profile, err = opensdsClient.CreateProfile(profile) - if err != nil { - reason := fmt.Sprintf("Create a volume type failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - result := converter.CreateTypeResp(profile) - body, err := json.Marshal(result) - if err != nil { - reason := fmt.Sprintf("Create a volume type, marshal result failed: %s", err.Error()) - portal.Ctx.Output.SetStatus(model.ErrorInternalServer) - portal.Ctx.Output.Body(model.ErrorInternalServerStatus(reason)) - log.Error(reason) - return - } - - portal.Ctx.Output.SetStatus(http.StatusOK) - portal.Ctx.Output.Body(body) - return -} diff --git a/contrib/cindercompatibleapi/api/volumetype_test.go b/contrib/cindercompatibleapi/api/volumetype_test.go deleted file mode 100644 index c94ca6c1f..000000000 --- a/contrib/cindercompatibleapi/api/volumetype_test.go +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright (c) 2018 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 api - -import ( - "bytes" - "encoding/json" - "net/http" - "net/http/httptest" - "reflect" - "testing" - - "github.com/astaxie/beego" - c "github.com/opensds/opensds/client" - "github.com/opensds/opensds/contrib/cindercompatibleapi/converter" -) - -func init() { - beego.Router("/v3/types", &TypePortal{}, - "post:CreateType;get:ListTypes") - beego.Router("/v3/types/:volumeTypeId", &TypePortal{}, - "get:GetType;delete:DeleteType;put:UpdateType") - beego.Router("/v3/types/:volumeTypeId/extra_specs", &TypePortal{}, - "post:AddExtraProperty;get:ListExtraProperties") - beego.Router("/v3/types/:volumeTypeId/extra_specs/:key", &TypePortal{}, - "get:ShowExtraProperty;put:UpdateExtraProperty;delete:DeleteExtraProperty") - - opensdsClient = c.NewFakeClient(&c.Config{Endpoint: c.TestEp}) -} - -//////////////////////////////////////////////////////////////////////////////// -// Tests for volume types // -//////////////////////////////////////////////////////////////////////////////// -func TestGetType(t *testing.T) { - r, _ := http.NewRequest("GET", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ShowTypeRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "volume_type": { - "id": "1106b972-66ef-11e7-b172-db03f3689c9c", - "name": "default", - "description": "default policy", - "extra_specs": [] - } - }` - - var expected converter.ShowTypeRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - expected.VolumeType.IsPublic = true - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestGetDefaultType(t *testing.T) { - DefaultTypeName = "default" - - r, _ := http.NewRequest("GET", "/v3/types/default", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ShowTypeRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if DefaultTypeName != output.VolumeType.Name { - t.Errorf("Expected %v, actual %v", DefaultTypeName, output.VolumeType.Description) - } -} - -func TestListTypes(t *testing.T) { - r, _ := http.NewRequest("GET", "/v3/types", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ListTypesRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedJSON := ` - { - "volume_types": [{ - "id": "1106b972-66ef-11e7-b172-db03f3689c9c", - "name": "default", - "description": "default policy", - "os-volume-type-access:is_public": true, - "is_public": true - }, - { - "id": "2f9c0a04-66ef-11e7-ade2-43158893e017", - "name": "silver", - "description": "silver policy", - "os-volume-type-access:is_public": true, - "is_public": true, - "extra_specs": { - "dataStorage": { - "provisioningPolicy": "Thin", - "isSpaceEfficient": true - }, - "ioConnectivity": { - "accessProtocol": "rbd", - "maxIOPS": 5000000, - "maxBWS": 500 - } - } - }] - }` - - var expected converter.ListTypesRespSpec - json.Unmarshal([]byte(expectedJSON), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestCreateType(t *testing.T) { - RequestBodyStr := ` - { - "volume_type": { - "name": "default", - "os-volume-type-access:is_public": true, - "description": "default policy" - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/v3/types", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.CreateTypeRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - var expected converter.CreateTypeRespSpec - json.Unmarshal([]byte(RequestBodyStr), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - expected.VolumeType.ID = "1106b972-66ef-11e7-b172-db03f3689c9c" - expected.VolumeType.IsPublic = true - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestCreateTypeWithBadRequest(t *testing.T) { - RequestBodyStr := ` - { - "volume_type": { - "name": "default", - "os-volume-type-access:is_public": true, - "description": "default policy", - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/v3/types", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - var output ErrorSpec - json.Unmarshal(w.Body.Bytes(), &output) - expected := "Create a volume type, parse request body failed: invalid character '}' looking for beginning of object key string" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } - - RequestBodyStr = ` - { - "volume_type": { - "name": "default", - "os-volume-type-access:is_public": false, - "description": "default policy" - } - }` - - jsonStr = []byte(RequestBodyStr) - r, _ = http.NewRequest("POST", "/v3/types", bytes.NewBuffer(jsonStr)) - - w = httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - json.Unmarshal(w.Body.Bytes(), &output) - expected = "Create a volume type failed: OpenSDS does not support os-volume-type-access:is_public = false" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } -} - -func TestDeleteType(t *testing.T) { - r, _ := http.NewRequest("DELETE", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c", nil) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusAccepted { - t.Errorf("Expected %v, actual %v", http.StatusAccepted, w.Code) - } -} - -func TestUpdateType(t *testing.T) { - RequestBodyStr := ` - { - "volume_type": { - "name": "default", - "description": "default policy", - "is_public": true - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("PUT", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.UpdateTypeRespSpec - json.Unmarshal(w.Body.Bytes(), &output) - - var expected converter.UpdateTypeRespSpec - json.Unmarshal([]byte(RequestBodyStr), &expected) - expected.VolumeType.IsPublic = true - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - expected.VolumeType.ID = "1106b972-66ef-11e7-b172-db03f3689c9c" - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestUpdateTypeWithBadRequest(t *testing.T) { - RequestBodyStr := ` - { - "volume_type": { - "name": "default", - "description": "default policy", - "is_public": true, - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("PUT", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - var output ErrorSpec - json.Unmarshal(w.Body.Bytes(), &output) - expected := "Update a volume type, parse request body failed: invalid character '}' looking for beginning of object key string" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } - - RequestBodyStr = ` - { - "volume_type": { - "name": "default", - "description": "default policy", - "is_public": false - } - }` - - jsonStr = []byte(RequestBodyStr) - r, _ = http.NewRequest("PUT", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c", bytes.NewBuffer(jsonStr)) - w = httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - json.Unmarshal(w.Body.Bytes(), &output) - expected = "Update a volume type failed: OpenSDS does not support is_public = false" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } -} - -func TestAddExtraProperty(t *testing.T) { - RequestBodyStr := ` - { - "extra_specs": { - "dataStorage": { - "provisioningPolicy": "Thin", - "isSpaceEfficient": true - }, - "ioConnectivity": { - "accessProtocol": "rbd", - "maxIOPS": 5000000, - "maxBWS": 500 - } - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c/extra_specs", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ExtraSpec - json.Unmarshal(w.Body.Bytes(), &output) - - var expected converter.ExtraSpec - json.Unmarshal([]byte(RequestBodyStr), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestAddExtraPropertyWithBadRequest(t *testing.T) { - RequestBodyStr := ` - { - "extra_specs": { - "dataStorage": { - "provisioningPolicy": "Thin", - "isSpaceEfficient": true - }, - "ioConnectivity": { - "accessProtocol": "rbd", - "maxIOPS": 5000000, - "maxBWS": 500, - } - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("POST", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c/extra_specs", bytes.NewBuffer(jsonStr)) - - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - var output ErrorSpec - json.Unmarshal(w.Body.Bytes(), &output) - expected := "Create or update extra specs for volume type, parse request body failed: invalid character '}' looking for beginning of object key string" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } -} - -func TestListExtraProperties(t *testing.T) { - r, _ := http.NewRequest("GET", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c/extra_specs", nil) - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ExtraSpec - json.Unmarshal(w.Body.Bytes(), &output) - - var expected converter.ExtraSpec - expectedStr := ` - { - "extra_specs": { - "dataStorage": { - "provisioningPolicy": "Thin", - "isSpaceEfficient": true - }, - "ioConnectivity": { - "accessProtocol": "rbd", - "maxIOPS": 5000000, - "maxBWS": 500 - } - } - }` - json.Unmarshal([]byte(expectedStr), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestShowExtraPropertie(t *testing.T) { - r, _ := http.NewRequest("GET", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c/extra_specs/dataStorage", nil) - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ExtraSpec - json.Unmarshal(w.Body.Bytes(), &output) - - var expected converter.ExtraSpec - expectedStr := ` - { - "dataStorage": { - "provisioningPolicy": "Thin", - "isSpaceEfficient": true - } - }` - json.Unmarshal([]byte(expectedStr), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestShowExtraPropertieWithBadRequest(t *testing.T) { - r, _ := http.NewRequest("GET", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c/extra_specs/disk", nil) - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ExtraSpec - json.Unmarshal(w.Body.Bytes(), &output) - - if w.Code != http.StatusNotFound { - t.Errorf("Expected %v, actual %v", http.StatusNotFound, w.Code) - } - - if nil != output["disk"] { - t.Errorf("Expected %v, actual %v", nil, output["disk"]) - } -} - -func TestUpdateExtraPropertiy(t *testing.T) { - RequestBodyStr := ` - { - "dataStorage": { - "provisioningPolicy": "Thin", - "isSpaceEfficient": true - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("PUT", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c/extra_specs/dataStorage", bytes.NewBuffer(jsonStr)) - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - var output converter.ExtraSpec - json.Unmarshal(w.Body.Bytes(), &output) - - expectedStr := ` - { - "dataStorage": { - "provisioningPolicy": "Thin", - "isSpaceEfficient": true - } - }` - - var expected converter.ExtraSpec - json.Unmarshal([]byte(expectedStr), &expected) - - if w.Code != http.StatusOK { - t.Errorf("Expected %v, actual %v", http.StatusOK, w.Code) - } - - if !reflect.DeepEqual(expected, output) { - t.Errorf("Expected %v, actual %v", expected, output) - } -} - -func TestUpdateExtraPropertiyWithBadRequest(t *testing.T) { - RequestBodyStr := ` - { - "dataStorage": { - "provisioningPolicy": "Thin", - "isSpaceEfficient": true, - } - }` - - var jsonStr = []byte(RequestBodyStr) - r, _ := http.NewRequest("PUT", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c/extra_specs/dataStorage", bytes.NewBuffer(jsonStr)) - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - var output ErrorSpec - json.Unmarshal(w.Body.Bytes(), &output) - expected := "Update extra specification for volume type, parse request body failed: invalid character '}' looking for beginning of object key string" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } - - RequestBodyStr = ` - { - "Storage": { - "provisioningPolicy": "Thin", - "isSpaceEfficient": true - } - }` - - jsonStr = []byte(RequestBodyStr) - r, _ = http.NewRequest("PUT", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c/extra_specs/dataStorage", bytes.NewBuffer(jsonStr)) - w = httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusBadRequest { - t.Errorf("Expected %v, actual %v", http.StatusBadRequest, w.Code) - } - - json.Unmarshal(w.Body.Bytes(), &output) - expected = "Update extra specification for volume type failed: The body of the request is wrong" - - if expected != output.Message { - t.Errorf("Expected %v, actual %v", expected, output.Message) - } -} - -func TestDeleteExtraPropertie(t *testing.T) { - r, _ := http.NewRequest("DELETE", "/v3/types/1106b972-66ef-11e7-b172-db03f3689c9c/extra_specs/diskType", nil) - w := httptest.NewRecorder() - beego.BeeApp.Handlers.ServeHTTP(w, r) - - if w.Code != http.StatusAccepted { - t.Errorf("Expected %v, actual %v", http.StatusAccepted, w.Code) - } -} diff --git a/contrib/cindercompatibleapi/converter/apiversion.go b/contrib/cindercompatibleapi/converter/apiversion.go deleted file mode 100644 index acb253f2a..000000000 --- a/contrib/cindercompatibleapi/converter/apiversion.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS northbound service. -*/ - -package converter - -import ( - "github.com/opensds/opensds/pkg/model" -) - -// *******************List All Api Versions******************* - -// ListAllAPIVersionsRespSpec ... -type ListAllAPIVersionsRespSpec struct { - Versions []ListAllAPIVersions `json:"versions"` -} - -// ListAllAPIVersions ... -type ListAllAPIVersions struct { - Status string `json:"status"` - Updated string `json:"updated"` - Links []VersionLink `json:"links"` - MinVersion string `json:"min_version,"` - Version string `json:"version"` - MediaTypes map[string]string `json:"media-types"` - ID string `json:"id"` -} - -// VersionLink ... -type VersionLink struct { - Href string `json:"href"` - Type string `json:"type"` - Rel string `json:"rel"` -} - -// ListAllAPIVersionsResp ... -func ListAllAPIVersionsResp(versions []*model.VersionSpec) *ListAllAPIVersionsRespSpec { - var resp ListAllAPIVersionsRespSpec - var cinderVersion ListAllAPIVersions - - if 0 == len(versions) { - resp.Versions = make([]ListAllAPIVersions, 0, 0) - } else { - for _, version := range versions { - - cinderVersion.Status = version.Status - cinderVersion.Updated = version.UpdatedAt - cinderVersion.MinVersion = "3.0" - cinderVersion.ID = "v3.0" - - resp.Versions = append(resp.Versions, cinderVersion) - - } - } - - return &resp -} diff --git a/contrib/cindercompatibleapi/converter/attachment.go b/contrib/cindercompatibleapi/converter/attachment.go deleted file mode 100644 index e88538262..000000000 --- a/contrib/cindercompatibleapi/converter/attachment.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS northbound service. -*/ - -package converter - -import ( - "github.com/opensds/opensds/pkg/model" -) - -// ExtraSpec ... -type ExtraSpec map[string]interface{} - -// ConnectionInfo ... -type ConnectionInfo struct { - DriverVolumeType string `json:"driver_volume_type,omitempty"` - ConnectionData map[string]interface{} `json:"data,omitempty"` -} - -// Connector ... -type Connector struct { - Initiator string `json:"initiator,omitempty"` - IP string `json:"ip,omitempty"` - Platform string `json:"platform,omitempty"` - Host string `json:"host,omitempty"` - OsType string `json:"os_type,omitempty"` - Multipath bool `json:"multipath,omitempty"` - Mountpoint string `json:"mountpoint,omitempty"` - Mode string `json:"mode,omitempty"` -} - -// *******************Show attachment details******************* - -// ShowAttachmentRespSpec ... -type ShowAttachmentRespSpec struct { - VolumeAttachment ShowRespAttachment `json:"attachment,omitempty"` -} - -// ShowRespAttachment ... -type ShowRespAttachment struct { - Status string `json:"status"` - DetachedAt string `json:"detached_at,omitempty"` - ConnectionInfo `json:"connection_info"` - AttachedAt string `json:"attached_at,omitempty"` - AttachMode string `json:"attach_mode,omitempty"` - Instance string `json:"instance,omitempty"` - VolumeID string `json:"volume_id"` - ID string `json:"id"` -} - -// ShowAttachmentResp ... -func ShowAttachmentResp(attachment *model.VolumeAttachmentSpec) *ShowAttachmentRespSpec { - resp := ShowAttachmentRespSpec{} - resp.VolumeAttachment.Status = attachment.Status - resp.VolumeAttachment.ConnectionInfo.DriverVolumeType = attachment.ConnectionInfo.DriverVolumeType - resp.VolumeAttachment.ConnectionInfo.ConnectionData = attachment.ConnectionInfo.ConnectionData - //resp.VolumeAttachment.AttachedAt = attachment.Mountpoint - resp.VolumeAttachment.Instance = attachment.Metadata["instance_uuid"] - resp.VolumeAttachment.VolumeID = attachment.VolumeId - resp.VolumeAttachment.ID = attachment.BaseModel.Id - - return &resp -} - -// *******************List attachments with details******************* - -// ListAttachmentsDetailsRespSpec ... -type ListAttachmentsDetailsRespSpec struct { - Attachments []ListRespAttachmentDetails `json:"attachments"` -} - -// ListRespAttachmentDetails ... -type ListRespAttachmentDetails struct { - Status string `json:"status"` - DetachedAt string `json:"detached_at,omitempty"` - ConnectionInfo ConnectionInfo `json:"connection_info"` - AttachedAt string `json:"attached_at,omitempty"` - AttachMode string `json:"attach_mode,omitempty"` - Instance string `json:"instance,omitempty"` - VolumeID string `json:"volume_id"` - ID string `json:"id"` -} - -// ListAttachmentsDetailsResp ... -func ListAttachmentsDetailsResp(attachments []*model.VolumeAttachmentSpec) *ListAttachmentsDetailsRespSpec { - var resp ListAttachmentsDetailsRespSpec - cinderAttachment := ListRespAttachmentDetails{} - - if 0 == len(attachments) { - // Even if the number is 0, it must return {"attachments":[]} - resp.Attachments = make([]ListRespAttachmentDetails, 0, 0) - } else { - for _, attachment := range attachments { - cinderAttachment.Status = attachment.Status - cinderAttachment.ConnectionInfo.DriverVolumeType = attachment.ConnectionInfo.DriverVolumeType - cinderAttachment.ConnectionInfo.ConnectionData = attachment.ConnectionInfo.ConnectionData - //cinderAttachment.AttachedAt = attachment.Mountpoint - cinderAttachment.Instance = attachment.Metadata["instance_uuid"] - cinderAttachment.VolumeID = attachment.VolumeId - cinderAttachment.ID = attachment.Id - - resp.Attachments = append(resp.Attachments, cinderAttachment) - } - } - - return &resp -} - -// *******************List attachments******************* - -// ListAttachmentsRespSpec ... -type ListAttachmentsRespSpec struct { - Attachments []ListRespAttachment `json:"attachments"` -} - -// ListRespAttachment ... -type ListRespAttachment struct { - Status string `json:"status"` - Instance string `json:"instance,omitempty"` - VolumeID string `json:"volume_id"` - ID string `json:"id"` -} - -// ListAttachmentsResp ... -func ListAttachmentsResp(attachments []*model.VolumeAttachmentSpec) *ListAttachmentsRespSpec { - var resp ListAttachmentsRespSpec - var cinderAttachment ListRespAttachment - - if 0 == len(attachments) { - // Even if the number is 0, it must return {"attachments":[]} - resp.Attachments = make([]ListRespAttachment, 0, 0) - } else { - for _, attachment := range attachments { - cinderAttachment.Status = attachment.Status - cinderAttachment.Instance = attachment.Metadata["instance_uuid"] - cinderAttachment.VolumeID = attachment.VolumeId - cinderAttachment.ID = attachment.Id - - resp.Attachments = append(resp.Attachments, cinderAttachment) - } - } - - return &resp -} - -// *******************Create attachment******************* - -// CreateAttachmentReqSpec ... -type CreateAttachmentReqSpec struct { - Attachment CreateReqAttachment `json:"attachment"` -} - -// CreateReqAttachment ... -type CreateReqAttachment struct { - InstanceUuID string `json:"instance_uuid"` - Connector Connector `json:"connector,omitempty"` - VolumeUuID string `json:"volume_uuid"` -} - -// CreateAttachmentRespSpec ... -type CreateAttachmentRespSpec struct { - Attachment CreateRespAttachment `json:"attachment"` -} - -// CreateRespAttachment ... -type CreateRespAttachment struct { - Status string `json:"status"` - DetachedAt string `json:"detached_at,omitempty"` - ConnectionInfo `json:"connection_info"` - AttachedAt string `json:"attached_at,omitempty"` - AttachMode string `json:"attach_mode,omitempty"` - Instance string `json:"instance,omitempty"` - VolumeID string `json:"volume_id"` - ID string `json:"id"` -} - -// CreateAttachmentReq ... -func CreateAttachmentReq(cinderReq *CreateAttachmentReqSpec) *model.VolumeAttachmentSpec { - attachment := model.VolumeAttachmentSpec{} - attachment.Metadata = make(map[string]string) - attachment.Metadata["instance_uuid"] = cinderReq.Attachment.InstanceUuID - attachment.HostInfo.Initiator = cinderReq.Attachment.Connector.Initiator - attachment.HostInfo.Ip = cinderReq.Attachment.Connector.IP - attachment.HostInfo.Platform = cinderReq.Attachment.Connector.Platform - attachment.HostInfo.Host = cinderReq.Attachment.Connector.Host - attachment.HostInfo.OsType = cinderReq.Attachment.Connector.OsType - attachment.Mountpoint = cinderReq.Attachment.Connector.Mountpoint - attachment.VolumeId = cinderReq.Attachment.VolumeUuID - - return &attachment -} - -// CreateAttachmentResp ... -func CreateAttachmentResp(attachment *model.VolumeAttachmentSpec) *CreateAttachmentRespSpec { - resp := CreateAttachmentRespSpec{} - resp.Attachment.Status = attachment.Status - resp.Attachment.ConnectionInfo.DriverVolumeType = attachment.ConnectionInfo.DriverVolumeType - resp.Attachment.ConnectionInfo.ConnectionData = attachment.ConnectionInfo.ConnectionData - //resp.Attachment.AttachedAt = attachment.Mountpoint - resp.Attachment.Instance = attachment.Metadata["instance_uuid"] - resp.Attachment.VolumeID = attachment.VolumeId - resp.Attachment.ID = attachment.BaseModel.Id - - return &resp -} - -// *******************Update an attachment******************* - -// UpdateAttachmentReqSpec ... -type UpdateAttachmentReqSpec struct { - Attachment UpdateReqAttachment `json:"attachment"` -} - -// UpdateReqAttachment ... -type UpdateReqAttachment struct { - Connector Connector `json:"connector"` -} - -// UpdateAttachmentRespSpec ... -type UpdateAttachmentRespSpec struct { - Attachment UpdateRespAttachment `json:"attachment"` -} - -// UpdateRespAttachment ... -type UpdateRespAttachment struct { - Status string `json:"status"` - DetachedAt string `json:"detached_at,omitempty"` - ConnectionInfo `json:"connection_info"` - AttachedAt string `json:"attached_at,omitempty"` - AttachMode string `json:"attach_mode,omitempty"` - Instance string `json:"instance,omitempty"` - VolumeID string `json:"volume_id"` - ID string `json:"id"` -} - -// UpdateAttachmentReq ... -func UpdateAttachmentReq(cinderReq *UpdateAttachmentReqSpec) *model.VolumeAttachmentSpec { - attachment := model.VolumeAttachmentSpec{} - attachment.HostInfo.Initiator = cinderReq.Attachment.Connector.Initiator - attachment.HostInfo.Ip = cinderReq.Attachment.Connector.IP - attachment.HostInfo.Platform = cinderReq.Attachment.Connector.Platform - attachment.HostInfo.Host = cinderReq.Attachment.Connector.Host - attachment.HostInfo.OsType = cinderReq.Attachment.Connector.OsType - attachment.Mountpoint = cinderReq.Attachment.Connector.Mountpoint - - return &attachment -} - -// UpdateAttachmentResp ... -func UpdateAttachmentResp(attachment *model.VolumeAttachmentSpec) *UpdateAttachmentRespSpec { - resp := UpdateAttachmentRespSpec{} - resp.Attachment.Status = attachment.Status - resp.Attachment.ConnectionInfo.DriverVolumeType = attachment.ConnectionInfo.DriverVolumeType - resp.Attachment.ConnectionInfo.ConnectionData = attachment.ConnectionInfo.ConnectionData - //resp.Attachment.AttachedAt = attachment.Mountpoint - resp.Attachment.Instance = attachment.Metadata["instance_uuid"] - resp.Attachment.VolumeID = attachment.VolumeId - resp.Attachment.ID = attachment.BaseModel.Id - - return &resp -} diff --git a/contrib/cindercompatibleapi/converter/snapshot.go b/contrib/cindercompatibleapi/converter/snapshot.go deleted file mode 100644 index 3b2859ebd..000000000 --- a/contrib/cindercompatibleapi/converter/snapshot.go +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS northbound service. -*/ - -package converter - -import ( - "errors" - - "github.com/opensds/opensds/pkg/model" -) - -// *******************Create a snapshot******************* - -// CreateSnapshotReqSpec ... -type CreateSnapshotReqSpec struct { - Snapshot CreateReqSnapshot `json:"snapshot"` -} - -// CreateReqSnapshot ... -type CreateReqSnapshot struct { - VolumeID string `json:"volume_id"` - Name string `json:"name"` - Description string `json:"description,omitempty"` - Force bool `json:"force,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// CreateSnapshotRespSpec ... -type CreateSnapshotRespSpec struct { - Snapshot CreateRespSnapshot `json:"snapshot,omitempty"` -} - -// CreateRespSnapshot ... -type CreateRespSnapshot struct { - Status string `json:"status"` - Description string `json:"description"` - CreatedAt string `json:"created_at"` - Name string `json:"name"` - UserID string `json:"user_id"` - VolumeID string `json:"volume_id"` - Metadata map[string]string `json:"metadata,omitempty"` - ID string `json:"id"` - Size int64 `json:"size"` - UpdatedAt string `json:"updated_at"` -} - -// CreateSnapshotReq ... -func CreateSnapshotReq(cinderReq *CreateSnapshotReqSpec) (*model.VolumeSnapshotSpec, error) { - req := model.VolumeSnapshotSpec{} - req.VolumeId = cinderReq.Snapshot.VolumeID - req.Name = cinderReq.Snapshot.Name - req.Description = cinderReq.Snapshot.Description - - if false != cinderReq.Snapshot.Force { - return nil, errors.New("OpenSDS does not support the parameter: force") - } - - if 0 != len(cinderReq.Snapshot.Metadata) { - return nil, errors.New("OpenSDS does not support the parameter: metadata") - } - - return &req, nil -} - -// CreateSnapshotResp ... -func CreateSnapshotResp(snapshot *model.VolumeSnapshotSpec) *CreateSnapshotRespSpec { - resp := CreateSnapshotRespSpec{} - resp.Snapshot.Status = snapshot.Status - resp.Snapshot.Description = snapshot.Description - resp.Snapshot.CreatedAt = snapshot.BaseModel.CreatedAt - resp.Snapshot.Name = snapshot.Name - resp.Snapshot.UserID = snapshot.UserId - resp.Snapshot.VolumeID = snapshot.VolumeId - //resp.Snapshot.Metadata = snapshot.Metadata - resp.Snapshot.ID = snapshot.BaseModel.Id - resp.Snapshot.Size = snapshot.Size - resp.Snapshot.UpdatedAt = snapshot.BaseModel.UpdatedAt - - return &resp -} - -// *******************Update a snapshot******************* - -// UpdateSnapshotReqSpec ... -type UpdateSnapshotReqSpec struct { - Snapshot UpdateReqSnapshot `json:"snapshot"` -} - -// UpdateReqSnapshot ... -type UpdateReqSnapshot struct { - Description string `json:"description,omitempty"` - Name string `json:"name"` -} - -// UpdateSnapshotRespSpec ... -type UpdateSnapshotRespSpec struct { - Snapshot UpdateRespSnapshot `json:"snapshot"` -} - -// UpdateRespSnapshot ... -type UpdateRespSnapshot struct { - Status string `json:"status"` - Description string `json:"description"` - CreatedAt string `json:"created_at"` - Name string `json:"name"` - ID string `json:"id"` - Size int64 `json:"size"` - VolumeID string `json:"volume_id"` - UserID string `json:"user_id"` -} - -// UpdateSnapshotReq ... -func UpdateSnapshotReq(cinderSnapshot *UpdateSnapshotReqSpec) *model.VolumeSnapshotSpec { - req := model.VolumeSnapshotSpec{} - req.Name = cinderSnapshot.Snapshot.Name - req.Description = cinderSnapshot.Snapshot.Description - - return &req -} - -// UpdateSnapshotResp ... -func UpdateSnapshotResp(snapshot *model.VolumeSnapshotSpec) *UpdateSnapshotRespSpec { - resp := UpdateSnapshotRespSpec{} - resp.Snapshot.Status = snapshot.Status - resp.Snapshot.Description = snapshot.Description - resp.Snapshot.CreatedAt = snapshot.BaseModel.CreatedAt - resp.Snapshot.Name = snapshot.Name - resp.Snapshot.ID = snapshot.BaseModel.Id - resp.Snapshot.Size = snapshot.Size - resp.Snapshot.VolumeID = snapshot.VolumeId - resp.Snapshot.UserID = snapshot.UserId - - return &resp -} - -// *******************Show a snapshot's details******************* - -// ShowSnapshotDetailsRespSpec ... -type ShowSnapshotDetailsRespSpec struct { - Snapshot ShowRespSnapshotDetails `json:"snapshot"` -} - -// ShowRespSnapshotDetails ... -type ShowRespSnapshotDetails struct { - Status string `json:"status"` - Description string `json:"description"` - CreatedAt string `json:"created_at"` - Name string `json:"name"` - UserID string `json:"user_id"` - VolumeID string `json:"volume_id"` - Size int64 `json:"size"` - ID string `json:"id"` -} - -// ShowSnapshotDetailsResp ... -func ShowSnapshotDetailsResp(snapshot *model.VolumeSnapshotSpec) *ShowSnapshotDetailsRespSpec { - resp := ShowSnapshotDetailsRespSpec{} - resp.Snapshot.Status = snapshot.Status - resp.Snapshot.Description = snapshot.Description - resp.Snapshot.CreatedAt = snapshot.BaseModel.CreatedAt - resp.Snapshot.Name = snapshot.Name - resp.Snapshot.UserID = snapshot.UserId - resp.Snapshot.VolumeID = snapshot.VolumeId - resp.Snapshot.Size = snapshot.Size - resp.Snapshot.ID = snapshot.BaseModel.Id - - return &resp -} - -// *******************List accessible snapshots******************* - -// ListSnapshotsRespSpec ... -type ListSnapshotsRespSpec struct { - Snapshots []ListRespSnapshot `json:"snapshots"` - Count int64 `json:"count,omitempty"` -} - -// ListRespSnapshot ... -type ListRespSnapshot struct { - Status string `json:"status"` - Description string `json:"description"` - CreatedAt string `json:"created_at"` - Name string `json:"name"` - UserID string `json:"user_id"` - VolumeID string `json:"volume_id"` - Metadata map[string]string `json:"metadata,omitempty"` - ID string `json:"id"` - Size int64 `json:"size"` -} - -// ListSnapshotsResp ... -func ListSnapshotsResp(snapshots []*model.VolumeSnapshotSpec) *ListSnapshotsRespSpec { - var resp ListSnapshotsRespSpec - var cinderSnapshot ListRespSnapshot - - if 0 == len(snapshots) { - resp.Snapshots = make([]ListRespSnapshot, 0, 0) - } else { - for _, snapshot := range snapshots { - cinderSnapshot.Status = snapshot.Status - cinderSnapshot.Description = snapshot.Description - cinderSnapshot.CreatedAt = snapshot.BaseModel.CreatedAt - cinderSnapshot.Name = snapshot.Name - cinderSnapshot.UserID = snapshot.UserId - cinderSnapshot.VolumeID = snapshot.VolumeId - //cinderSnapshot.Metadata = snapshot.Metadata - cinderSnapshot.ID = snapshot.BaseModel.Id - cinderSnapshot.Size = snapshot.Size - - resp.Snapshots = append(resp.Snapshots, cinderSnapshot) - } - } - - return &resp -} - -// *******************List snapshots and details******************* - -// ListSnapshotsDetailsRespSpec ... -type ListSnapshotsDetailsRespSpec struct { - Snapshots []ListRespSnapshotDetails `json:"snapshots"` - Count int64 `json:"count,omitempty"` -} - -// ListRespSnapshotDetails ... -type ListRespSnapshotDetails struct { - Status string `json:"status"` - Progress int64 `json:"os-extended-snapshot-attributes:progress,omitempty"` - Description string `json:"description"` - CreatedAt string `json:"created_at"` - Name string `json:"name"` - UserID string `json:"user_id"` - VolumeID string `json:"volume_id"` - ProjectID string `json:"os-extended-snapshot-attributes:project_id,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` - ID string `json:"id"` - Size int64 `json:"size"` -} - -// ListSnapshotsDetailsResp ... -func ListSnapshotsDetailsResp(snapshots []*model.VolumeSnapshotSpec) *ListSnapshotsDetailsRespSpec { - var resp ListSnapshotsDetailsRespSpec - var cinderSnapshot ListRespSnapshotDetails - - if 0 == len(snapshots) { - resp.Snapshots = make([]ListRespSnapshotDetails, 0, 0) - } else { - for _, snapshot := range snapshots { - cinderSnapshot.Status = snapshot.Status - cinderSnapshot.Description = snapshot.Description - cinderSnapshot.CreatedAt = snapshot.BaseModel.CreatedAt - cinderSnapshot.Name = snapshot.Name - cinderSnapshot.UserID = snapshot.UserId - cinderSnapshot.VolumeID = snapshot.VolumeId - //cinderSnapshot.Metadata = snapshot.Metadata - cinderSnapshot.ID = snapshot.BaseModel.Id - cinderSnapshot.Size = snapshot.Size - - resp.Snapshots = append(resp.Snapshots, cinderSnapshot) - } - } - - return &resp -} diff --git a/contrib/cindercompatibleapi/converter/volume.go b/contrib/cindercompatibleapi/converter/volume.go deleted file mode 100644 index 1b5e3cf7a..000000000 --- a/contrib/cindercompatibleapi/converter/volume.go +++ /dev/null @@ -1,456 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS northbound service. -*/ - -package converter - -import ( - "errors" - - "github.com/opensds/opensds/pkg/model" -) - -var ( - // APIVersion ... - APIVersion = "v3" - // Endpoint ... - Endpoint = "http://127.0.0.1:8777/v3" -) - -// *******************List accessible volumes with details******************* - -// ListVolumesDetailsRespSpec ... -type ListVolumesDetailsRespSpec struct { - Volumes []ListRespVolumeDetails `json:"volumes"` - Count int64 `json:"count,omitempty"` -} - -// ListRespVolumeDetails ... -type ListRespVolumeDetails struct { - MigrationStatus string `json:"migration_status,omitempty"` - Attachments []RespAttachment `json:"attachments"` - Links []Link `json:"links,omitempty"` - AvailabilityZone string `json:"availability_zone,omitempty"` - Host string `json:"os-vol-host-attr:host,omitempty"` - Encrypted bool `json:"encrypted,omitempty"` - UpdatedAt string `json:"updated_at"` - ReplicationStatus string `json:"replication_status,omitempty"` - SnapshotID string `json:"snapshot_id,omitempty"` - ID string `json:"id"` - Size int64 `json:"size"` - UserID string `json:"user_id"` - TenantID string `json:"os-vol-tenant-attr:tenant_id,omitempty"` - Migstat string `json:"os-vol-mig-status-attr:migstat,omitempty"` - Metadata map[string]string `json:"metadata"` - Status string `json:"status"` - VolumeImageMetadata map[string]string `json:"volume_image_metadata,omitempty"` - Description string `json:"description"` - Multiattach bool `json:"multiattach,omitempty"` - SourceVolID string `json:"source_volid,omitempty"` - ConsistencygroupID string `json:"consistencygroup_id,omitempty"` - NameID string `json:"os-vol-mig-status-attr:name_id,omitempty"` - Name string `json:"name"` - Bootable bool `json:"bootable,omitempty"` - CreatedAt string `json:"created_at"` - VolumeType string `json:"volume_type,omitempty"` -} - -// ListVolumesDetailsResp ... -func ListVolumesDetailsResp(volumes []*model.VolumeSpec) *ListVolumesDetailsRespSpec { - var resp ListVolumesDetailsRespSpec - var cinderVolume ListRespVolumeDetails - - if 0 == len(volumes) { - resp.Volumes = make([]ListRespVolumeDetails, 0, 0) - } else { - for _, volume := range volumes { - - cinderVolume.Attachments = make([]RespAttachment, 0, 0) - cinderVolume.AvailabilityZone = volume.AvailabilityZone - cinderVolume.UpdatedAt = volume.BaseModel.UpdatedAt - cinderVolume.ID = volume.BaseModel.Id - cinderVolume.Size = volume.Size - cinderVolume.UserID = volume.UserId - cinderVolume.Metadata = make(map[string]string) - //cinderVolume.TenantID = volume.TenantId - cinderVolume.Status = volume.Status - cinderVolume.Description = volume.Description - cinderVolume.Name = volume.Name - cinderVolume.CreatedAt = volume.BaseModel.CreatedAt - cinderVolume.VolumeType = volume.ProfileId - - resp.Volumes = append(resp.Volumes, cinderVolume) - } - } - - return &resp -} - -// *******************Create a volume******************* - -// CreateVolumeReqSpec ... -type CreateVolumeReqSpec struct { - Volume CreateReqVolume `json:"volume"` - SchedulerHints SchedulerHints `json:"OS-SCH-HNT:scheduler_hints,omitempty"` -} - -// CreateReqVolume ... -type CreateReqVolume struct { - Size int64 `json:"size"` - AvailabilityZone string `json:"availability_zone,omitempty"` - SourceVolID string `json:"source_volid,omitempty"` - Description string `json:"description,omitempty"` - Multiattach bool `json:"multiattach,omitempty"` - SnapshotID string `json:"snapshot_id,omitempty"` - BackupID string `json:"backup_id,omitempty"` - Name string `json:"name"` - ImageRef string `json:"imageRef,omitempty"` - VolumeType string `json:"volume_type,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` - ConsistencygroupID string `json:"consistencygroup_id,omitempty"` -} - -// SchedulerHints ... -type SchedulerHints struct { - SameHost []string `json:"same_host,omitempty"` -} - -// CreateVolumeRespSpec ... -type CreateVolumeRespSpec struct { - Volume CreateRespVolume `json:"volume,omitempty"` -} - -// CreateRespVolume ... -type CreateRespVolume struct { - MigrationStatus string `json:"migration_status,omitempty"` - Attachments []RespAttachment `json:"attachments"` - Links []Link `json:"links,omitempty"` - AvailabilityZone string `json:"availability_zone,omitempty"` - Encrypted bool `json:"encrypted,omitempty"` - UpdatedAt string `json:"updated_at,omitempty"` - ReplicationStatus string `json:"replication_status,omitempty"` - SnapshotID string `json:"snapshot_id,omitempty"` - ID string `json:"id,omitempty"` - Size int64 `json:"size,omitempty"` - UserID string `json:"user_id,omitempty"` - Metadata map[string]string `json:"metadata"` - Status string `json:"status,omitempty"` - Description string `json:"description,omitempty"` - Multiattach bool `json:"multiattach,omitempty"` - SourceVolID string `json:"source_volid,omitempty"` - ConsistencygroupID string `json:"consistencygroup_id,omitempty"` - Name string `json:"name,omitempty"` - Bootable bool `json:"bootable,omitempty"` - CreatedAt string `json:"created_at,omitempty"` - VolumeType string `json:"volume_type,omitempty"` -} - -// Link ... -type Link struct { - Href string `json:"href,omitempty"` - Rel string `json:"rel,omitempty"` -} - -// CreateVolumeReq ... -func CreateVolumeReq(cinderReq *CreateVolumeReqSpec) (*model.VolumeSpec, error) { - volume := model.VolumeSpec{} - volume.BaseModel = &model.BaseModel{} - volume.Name = cinderReq.Volume.Name - volume.Description = cinderReq.Volume.Description - volume.Size = cinderReq.Volume.Size - volume.AvailabilityZone = cinderReq.Volume.AvailabilityZone - volume.ProfileId = cinderReq.Volume.VolumeType - - if ("" != cinderReq.Volume.SourceVolID) || (false != cinderReq.Volume.Multiattach) || - ("" != cinderReq.Volume.SnapshotID) || ("" != cinderReq.Volume.BackupID) || - ("" != cinderReq.Volume.ImageRef) || (0 != len(cinderReq.Volume.Metadata)) || - ("" != cinderReq.Volume.ConsistencygroupID) { - - return nil, errors.New("OpenSDS does not support the parameter: " + - "id/source_volid/multiattach/snapshot_id/backup_id/imageRef/metadata/consistencygroup_id") - } - - return &volume, nil -} - -// CreateVolumeResp ... -func CreateVolumeResp(volume *model.VolumeSpec) *CreateVolumeRespSpec { - resp := CreateVolumeRespSpec{} - - resp.Volume.Attachments = make([]RespAttachment, 0, 0) - resp.Volume.AvailabilityZone = volume.AvailabilityZone - resp.Volume.UpdatedAt = volume.BaseModel.UpdatedAt - resp.Volume.ID = volume.BaseModel.Id - resp.Volume.Size = volume.Size - resp.Volume.UserID = volume.UserId - resp.Volume.Metadata = make(map[string]string) - resp.Volume.Status = volume.Status - resp.Volume.Description = volume.Description - resp.Volume.Name = volume.Name - resp.Volume.CreatedAt = volume.BaseModel.CreatedAt - resp.Volume.VolumeType = volume.ProfileId - - return &resp -} - -// *******************List accessible volumes******************* - -// ListVolumesRespSpec ... -type ListVolumesRespSpec struct { - Volumes []ListRespVolume `json:"volumes"` - Count int64 `json:"count,omitempty"` -} - -// ListRespVolume ... -type ListRespVolume struct { - ID string `json:"id"` - Links []Link `json:"links,omitempty"` - Name string `json:"name"` -} - -// ListVolumesResp ... -func ListVolumesResp(volumes []*model.VolumeSpec) *ListVolumesRespSpec { - var resp ListVolumesRespSpec - var cinderVolume ListRespVolume - - if 0 == len(volumes) { - resp.Volumes = make([]ListRespVolume, 0, 0) - } else { - for _, volume := range volumes { - cinderVolume.ID = volume.Id - cinderVolume.Name = volume.Name - - resp.Volumes = append(resp.Volumes, cinderVolume) - } - } - - return &resp -} - -// *******************Show a volume's details******************* - -// ShowVolumeRespSpec ... -type ShowVolumeRespSpec struct { - Volume ShowRespVolume `json:"volume"` -} - -// ShowRespVolume ... -type ShowRespVolume struct { - MigrationStatus string `json:"migration_status,omitempty"` - Attachments []RespAttachment `json:"attachments"` - Links []Link `json:"links,omitempty"` - AvailabilityZone string `json:"availability_zone,omitempty"` - Host string `json:"os-vol-host-attr:host,omitempty"` - Encrypted bool `json:"encrypted,omitempty"` - UpdatedAt string `json:"updated_at"` - ReplicationStatus string `json:"replication_status,omitempty"` - SnapshotID string `json:"snapshot_id"` - ID string `json:"id"` - Size int64 `json:"size"` - UserID string `json:"user_id"` - TenantID string `json:"os-vol-tenant-attr:tenant_id,omitempty"` - Migstat string `json:"os-vol-mig-status-attr:migstat,omitempty"` - Metadata map[string]string `json:"metadata"` - Status string `json:"status"` - VolumeImageMetadata map[string]string `json:"volume_image_metadata"` - Description string `json:"description"` - Multiattach bool `json:"multiattach,omitempty"` - SourceVolID string `json:"source_volid,omitempty"` - ConsistencygroupID string `json:"consistencygroup_id,omitempty"` - NameID string `json:"os-vol-mig-status-attr:name_id,omitempty"` - Name string `json:"name"` - Bootable bool `json:"bootable"` - CreatedAt string `json:"created_at"` - VolumeType string `json:"volume_type,omitempty"` - ServiceUuID string `json:"service_uuid,omitempty"` - SharedTargets bool `json:"shared_targets,omitempty"` -} - -// ShowVolumeResp ... -func ShowVolumeResp(volume *model.VolumeSpec) *ShowVolumeRespSpec { - resp := ShowVolumeRespSpec{} - - resp.Volume.Attachments = make([]RespAttachment, 0, 0) - resp.Volume.AvailabilityZone = volume.AvailabilityZone - resp.Volume.UpdatedAt = volume.BaseModel.UpdatedAt - resp.Volume.ID = volume.BaseModel.Id - resp.Volume.Size = volume.Size - resp.Volume.UserID = volume.UserId - resp.Volume.Metadata = make(map[string]string) - resp.Volume.Status = volume.Status - resp.Volume.Description = volume.Description - resp.Volume.Name = volume.Name - resp.Volume.CreatedAt = volume.BaseModel.CreatedAt - resp.Volume.VolumeType = volume.ProfileId - //resp.Volume.TenantID = volume.TenantId - - return &resp -} - -// *******************Update a volume******************* - -// UpdateVolumeReqSpec ... -type UpdateVolumeReqSpec struct { - Volume UpdateReqVolume `json:"volume"` -} - -// UpdateReqVolume ... -type UpdateReqVolume struct { - Description string `json:"description,omitempty"` - Name string `json:"name,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// UpdateVolumeRespSpec ... -type UpdateVolumeRespSpec struct { - Volume UpdateRespVolume `json:"volume"` -} - -// UpdateRespVolume ... -type UpdateRespVolume struct { - MigrationStatus string `json:"migration_status,omitempty"` - Attachments []RespAttachment `json:"attachments"` - Links []Link `json:"links,omitempty"` - AvailabilityZone string `json:"availability_zone,omitempty"` - Encrypted bool `json:"encrypted,omitempty"` - UpdatedAt string `json:"updated_at"` - ReplicationStatus string `json:"replication_status,omitempty"` - SnapshotID string `json:"snapshot_id,omitempty"` - ID string `json:"id"` - Size int64 `json:"size"` - UserID string `json:"user_id"` - Metadata map[string]string `json:"metadata"` - Status string `json:"status"` - Description string `json:"description"` - Multiattach bool `json:"multiattach,omitempty"` - SourceVolID string `json:"source_volid,omitempty"` - ConsistencygroupID string `json:"consistencygroup_id,omitempty"` - Name string `json:"name"` - Bootable bool `json:"bootable,omitempty"` - CreatedAt string `json:"created_at"` - VolumeType string `json:"volume_type,omitempty"` -} - -// RespAttachment ... -type RespAttachment struct { - ID string `json:"id,omitempty"` -} - -// UpdateVolumeReq ... -func UpdateVolumeReq(cinderReq *UpdateVolumeReqSpec) (*model.VolumeSpec, error) { - volume := model.VolumeSpec{} - volume.BaseModel = &model.BaseModel{} - volume.Description = cinderReq.Volume.Description - volume.Name = cinderReq.Volume.Name - - if 0 != len(cinderReq.Volume.Metadata) { - - return nil, errors.New("OpenSDS does not support the parameter: metadata") - } - - return &volume, nil -} - -// UpdateVolumeResp ... -func UpdateVolumeResp(volume *model.VolumeSpec) *UpdateVolumeRespSpec { - resp := UpdateVolumeRespSpec{} - resp.Volume.Attachments = make([]RespAttachment, 0, 0) - resp.Volume.AvailabilityZone = volume.AvailabilityZone - resp.Volume.UpdatedAt = volume.BaseModel.UpdatedAt - resp.Volume.ID = volume.BaseModel.Id - resp.Volume.Size = volume.Size - resp.Volume.UserID = volume.UserId - resp.Volume.Metadata = make(map[string]string) - resp.Volume.Status = volume.Status - resp.Volume.Description = volume.Description - resp.Volume.Name = volume.Name - resp.Volume.CreatedAt = volume.BaseModel.CreatedAt - resp.Volume.VolumeType = volume.ProfileId - - return &resp -} - -// *******************Volume actions******************* - -// InitializeConnectionReqSpec ... -type InitializeConnectionReqSpec struct { - InitializeConnection InitializeConnection `json:"os-initialize_connection"` -} - -// InitializeConnection ... -type InitializeConnection struct { - Connector InitializeConnector `json:"connector"` -} - -// InitializeConnector ... -type InitializeConnector struct { - Platform string `json:"platform"` - Host string `json:"host"` - DoLocalAttach bool `json:"do_local_attach"` - IP string `json:"ip"` - OsType string `json:"os_type"` - Multipath bool `json:"multipath"` - Initiator string `json:"initiator"` -} - -// InitializeConnectionRespSpec ... -type InitializeConnectionRespSpec struct { - ConnectionInfo InitializeConnectionInfo `json:"connection_info"` -} - -// InitializeConnectionInfo ... -type InitializeConnectionInfo struct { - DriverVolumeType string `json:"driver_volume_type"` - Data map[string]interface{} `json:"data"` -} - -// InitializeConnectionReq ... -func InitializeConnectionReq(initializeConnectionReq *InitializeConnectionReqSpec, volumeID string) *model.VolumeAttachmentSpec { - attachment := model.VolumeAttachmentSpec{} - attachment.Metadata = make(map[string]string) - //attachment.Metadata["instance_uuid"] = cinderReq.Attachment.InstanceUuID - attachment.HostInfo.Initiator = initializeConnectionReq.InitializeConnection.Connector.Initiator - attachment.HostInfo.Ip = initializeConnectionReq.InitializeConnection.Connector.IP - attachment.HostInfo.Platform = initializeConnectionReq.InitializeConnection.Connector.Platform - attachment.HostInfo.Host = initializeConnectionReq.InitializeConnection.Connector.Host - attachment.HostInfo.OsType = initializeConnectionReq.InitializeConnection.Connector.OsType - //attachment.Mountpoint = cinderReq.Attachment.Connector.Mountpoint - attachment.VolumeId = volumeID - - return &attachment -} - -// InitializeConnectionResp ... -func InitializeConnectionResp(attachment *model.VolumeAttachmentSpec) *InitializeConnectionRespSpec { - resp := InitializeConnectionRespSpec{} - resp.ConnectionInfo.DriverVolumeType = attachment.ConnectionInfo.DriverVolumeType - resp.ConnectionInfo.Data = make(map[string]interface{}) - - resp.ConnectionInfo.Data["auth_password"] = attachment.ConnectionInfo.ConnectionData["authPassword"] - resp.ConnectionInfo.Data["target_discovered"] = attachment.ConnectionInfo.ConnectionData["targetDiscovered"] - resp.ConnectionInfo.Data["encrypted"] = attachment.ConnectionInfo.ConnectionData["encrypted"] - //resp.ConnectionInfo.Data["qos_specs"] - resp.ConnectionInfo.Data["target_iqn"] = attachment.ConnectionInfo.ConnectionData["targetIQN"] - resp.ConnectionInfo.Data["target_portal"] = attachment.ConnectionInfo.ConnectionData["targetPortal"] - resp.ConnectionInfo.Data["volume_id"] = attachment.ConnectionInfo.ConnectionData["volumeId"] - resp.ConnectionInfo.Data["target_lun"] = attachment.ConnectionInfo.ConnectionData["targetLun"] - resp.ConnectionInfo.Data["access_mode"] = attachment.ConnectionInfo.ConnectionData["accessMode"] - resp.ConnectionInfo.Data["auth_username"] = attachment.ConnectionInfo.ConnectionData["authUserName"] - resp.ConnectionInfo.Data["auth_method"] = attachment.ConnectionInfo.ConnectionData["authMethod"] - - return &resp -} diff --git a/contrib/cindercompatibleapi/converter/volumetype.go b/contrib/cindercompatibleapi/converter/volumetype.go deleted file mode 100644 index 4da7f0ca4..000000000 --- a/contrib/cindercompatibleapi/converter/volumetype.go +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS northbound service. -*/ - -package converter - -import ( - "errors" - - "github.com/opensds/opensds/pkg/model" -) - -// *******************Create a volume type******************* - -// CreateTypeReqSpec ... -type CreateTypeReqSpec struct { - VolumeType CreateReqVolumeType `json:"volume_type,omitempty"` -} - -// CreateReqVolumeType ... -type CreateReqVolumeType struct { - Name string `json:"name"` - AccessIsPublic bool `json:"os-volume-type-access:is_public,omitempty"` - Description string `json:"description,omitempty"` - Extras ExtraSpec `json:"extra_specs,omitempty"` -} - -// CreateTypeRespSpec ... -type CreateTypeRespSpec struct { - VolumeType CreateRespVolumeType `json:"volume_type,omitempty"` -} - -// CreateRespVolumeType ... -type CreateRespVolumeType struct { - IsPublic bool `json:"is_public,omitempty"` - Extras ExtraSpec `json:"extra_specs,omitempty"` - Description string `json:"description,omitempty"` - Name string `json:"name,omitempty"` - ID string `json:"id,omitempty"` - AccessIsPublic bool `json:"os-volume-type-access:is_public,omitempty"` -} - -// CreateTypeReq ... -func CreateTypeReq(cinderReq *CreateTypeReqSpec) (*model.ProfileSpec, error) { - profile := model.ProfileSpec{} - - profile.Name = cinderReq.VolumeType.Name - if false == cinderReq.VolumeType.AccessIsPublic { - return nil, errors.New("OpenSDS does not support os-volume-type-access:is_public = false") - } - profile.Description = cinderReq.VolumeType.Description - profile.CustomProperties = *(CinderExtraToOpenSDSExtra(&(cinderReq.VolumeType.Extras))) - - // The storageType can be block, file, object, default is block - profile.StorageType = "block" - - return &profile, nil -} - -// CinderExtraToOpenSDSExtra ... -func CinderExtraToOpenSDSExtra(typeExtra *ExtraSpec) *model.CustomPropertiesSpec { - var profileExtras model.CustomPropertiesSpec - profileExtras = make(map[string]interface{}) - for key, value := range *typeExtra { - profileExtras[key] = value - } - - return &profileExtras -} - -// CreateTypeResp ... -func CreateTypeResp(profile *model.ProfileSpec) *CreateTypeRespSpec { - resp := CreateTypeRespSpec{} - resp.VolumeType.IsPublic = true - resp.VolumeType.Extras = *(OpenSDSExtraToCinderExtra(&(profile.CustomProperties))) - resp.VolumeType.Description = profile.Description - resp.VolumeType.Name = profile.Name - resp.VolumeType.ID = profile.BaseModel.Id - resp.VolumeType.AccessIsPublic = true - - return &resp -} - -// *******************Update a volume type******************* - -// UpdateTypeReqSpec ... -type UpdateTypeReqSpec struct { - VolumeType UpdateReqVolumeType `json:"volume_type"` -} - -// UpdateReqVolumeType ... -type UpdateReqVolumeType struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - IsPublic bool `json:"is_public,omitempty"` -} - -// UpdateTypeRespSpec ... -type UpdateTypeRespSpec struct { - VolumeType UpdateRespVolumeType `json:"volume_type"` -} - -// UpdateRespVolumeType ... -type UpdateRespVolumeType struct { - IsPublic bool `json:"is_public"` - Extras ExtraSpec `json:"extra_specs"` - Description string `json:"description"` - Name string `json:"name"` - ID string `json:"id"` -} - -// UpdateTypeReq ... -func UpdateTypeReq(cinderReq *UpdateTypeReqSpec) (*model.ProfileSpec, error) { - profile := model.ProfileSpec{} - profile.Name = cinderReq.VolumeType.Name - profile.Description = cinderReq.VolumeType.Description - if false == cinderReq.VolumeType.IsPublic { - return nil, errors.New("OpenSDS does not support is_public = false") - } - - return &profile, nil -} - -// UpdateTypeResp ... -func UpdateTypeResp(profile *model.ProfileSpec) *UpdateTypeRespSpec { - resp := UpdateTypeRespSpec{} - resp.VolumeType.IsPublic = true - resp.VolumeType.Extras = *(OpenSDSExtraToCinderExtra(&(profile.CustomProperties))) - resp.VolumeType.Description = profile.Description - resp.VolumeType.Name = profile.Name - resp.VolumeType.ID = profile.BaseModel.Id - - return &resp -} - -// *******************Create or update extra specs for volume type******************* - -// AddExtraReqSpec ... -type AddExtraReqSpec struct { - Extras ExtraSpec `json:"extra_specs"` -} - -// AddExtraRespSpec ... -type AddExtraRespSpec struct { - Extras ExtraSpec `json:"extra_specs"` -} - -// AddExtraReq ... -func AddExtraReq(cinderReq *AddExtraReqSpec) *model.CustomPropertiesSpec { - var profileExtras model.CustomPropertiesSpec - - if len(cinderReq.Extras) >= 1 { - profileExtras = make(map[string]interface{}) - for key, value := range cinderReq.Extras { - profileExtras[key] = value - } - } - - return &profileExtras -} - -// AddExtraResp ... -func AddExtraResp(profileExtras *model.CustomPropertiesSpec) *AddExtraRespSpec { - var resp AddExtraRespSpec - - if len(*profileExtras) >= 1 { - resp.Extras = make(map[string]interface{}) - for key, value := range *profileExtras { - resp.Extras[key] = value - } - } - - return &resp -} - -// *******************Show all extra specifications for volume type******************* - -// ShowAllExtraRespSpec ... -type ShowAllExtraRespSpec struct { - Extras ExtraSpec `json:"extra_specs"` -} - -// ShowAllExtraResp ... -func ShowAllExtraResp(profileExtras *model.CustomPropertiesSpec) *ShowAllExtraRespSpec { - var resp ShowAllExtraRespSpec - - if len(*profileExtras) >= 1 { - resp.Extras = make(map[string]interface{}) - for key, value := range *profileExtras { - resp.Extras[key] = value - } - } - - return &resp -} - -// *******************Show extra specification for volume type******************* - -// ShowExtraRespSpec ... -type ShowExtraRespSpec map[string]interface{} - -//ShowExtraResp ... -func ShowExtraResp(reqkey string, profileExtras *model.CustomPropertiesSpec) *ShowExtraRespSpec { - var resp ShowExtraRespSpec - - if (len(*profileExtras) >= 1) && (nil != (*profileExtras)[reqkey]) { - resp = make(map[string]interface{}) - resp[reqkey] = (*profileExtras)[reqkey] - } - - return &resp -} - -// *******************Update extra specification for volume type******************* - -// UpdateExtraReqSpec ... -type UpdateExtraReqSpec map[string]interface{} - -// UpdateExtraRespSpec ... -type UpdateExtraRespSpec map[string]interface{} - -// UpdateExtraReq ... -func UpdateExtraReq(reqkey string, cinderReq *UpdateExtraReqSpec) (*model.CustomPropertiesSpec, error) { - var profileExtras model.CustomPropertiesSpec - - if (1 == len(*cinderReq)) && (nil != (*cinderReq)[reqkey]) { - profileExtras = make(map[string]interface{}) - profileExtras[reqkey] = (*cinderReq)[reqkey] - } else { - return nil, errors.New("The body of the request is wrong") - } - - return &profileExtras, nil -} - -// UpdateExtraResp ... -func UpdateExtraResp(reqkey string, profileExtras *model.CustomPropertiesSpec) *UpdateExtraRespSpec { - var resp UpdateExtraRespSpec - - if (len(*profileExtras) >= 1) && (nil != (*profileExtras)[reqkey]) { - resp = make(map[string]interface{}) - resp[reqkey] = (*profileExtras)[reqkey] - } - - return &resp -} - -// *******************Show volume type detail******************* - -// ShowTypeRespSpec ... -type ShowTypeRespSpec struct { - VolumeType ShowRespVolumeType `json:"volume_type"` -} - -// ShowRespVolumeType ... -type ShowRespVolumeType struct { - IsPublic bool `json:"is_public"` - Extras ExtraSpec `json:"extra_specs"` - Description string `json:"description"` - Name string `json:"name"` - ID string `json:"id"` -} - -// ShowTypeResp ... -func ShowTypeResp(profile *model.ProfileSpec) *ShowTypeRespSpec { - resp := ShowTypeRespSpec{} - resp.VolumeType.IsPublic = true - resp.VolumeType.Extras = *(OpenSDSExtraToCinderExtra(&(profile.CustomProperties))) - resp.VolumeType.Description = profile.Description - resp.VolumeType.Name = profile.Name - resp.VolumeType.ID = profile.BaseModel.Id - - return &resp -} - -// *******************List all volume types******************* - -// ListTypesRespSpec ... -type ListTypesRespSpec struct { - VolumeTypes []ListRespVolumeType `json:"volume_types"` -} - -// ListRespVolumeType ... -type ListRespVolumeType struct { - Extras ExtraSpec `json:"extra_specs"` - Name string `json:"name"` - AccessIsPublic bool `json:"os-volume-type-access:is_public"` - IsPublic bool `json:"is_public"` - ID string `json:"id"` - Description string `json:"description"` -} - -// ListTypesResp ... -func ListTypesResp(profiles []*model.ProfileSpec) *ListTypesRespSpec { - var resp ListTypesRespSpec - var volumeType ListRespVolumeType - - if 0 == len(profiles) { - resp.VolumeTypes = make([]ListRespVolumeType, 0, 0) - } else { - for _, profile := range profiles { - volumeType.Extras = *(OpenSDSExtraToCinderExtra(&(profile.CustomProperties))) - volumeType.Name = profile.Name - volumeType.AccessIsPublic = true - volumeType.IsPublic = true - volumeType.ID = profile.BaseModel.Id - volumeType.Description = profile.Description - - resp.VolumeTypes = append(resp.VolumeTypes, volumeType) - } - } - - return &resp -} - -// OpenSDSExtraToCinderExtra ... -func OpenSDSExtraToCinderExtra(profileExtras *model.CustomPropertiesSpec) *ExtraSpec { - var typeExtra ExtraSpec - - if len(*profileExtras) >= 1 { - typeExtra = make(map[string]interface{}) - for key, value := range *profileExtras { - typeExtra[key] = value - } - } - - return &typeExtra -} diff --git a/contrib/cindercompatibleapi/main.go b/contrib/cindercompatibleapi/main.go deleted file mode 100644 index e12a8488b..000000000 --- a/contrib/cindercompatibleapi/main.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2018 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. - -/* -This module implements a entry into the OpenSDS REST service. - -*/ - -package main - -import ( - "flag" - "fmt" - "os" - "time" - - "github.com/opensds/opensds/contrib/cindercompatibleapi/api" - "github.com/opensds/opensds/pkg/utils/logs" -) - -func main() { - flag.Parse() - logs.InitLogs(5 * time.Second) - defer logs.FlushLogs() - - cinderEndpoint, ok := os.LookupEnv("CINDER_ENDPOINT") - if !ok { - fmt.Println("ERROR: You must provide the cinder endpoint by setting " + - "the environment variable CINDER_ENDPOINT") - return - } - - api.Run(cinderEndpoint) -} From ccc2ab88754b1b14d589cf833fd1d8b5892460c1 Mon Sep 17 00:00:00 2001 From: BaiHuoYu Date: Wed, 5 Dec 2018 16:08:23 +0800 Subject: [PATCH 4/7] Adapt the message of the new version "ceph df" command --- contrib/drivers/ceph/ceph.go | 114 +++++++++++++++++++++++++++++------ 1 file changed, 95 insertions(+), 19 deletions(-) diff --git a/contrib/drivers/ceph/ceph.go b/contrib/drivers/ceph/ceph.go index 7ff30c1d7..de2037c88 100755 --- a/contrib/drivers/ceph/ceph.go +++ b/contrib/drivers/ceph/ceph.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "os/exec" + "regexp" "strconv" "strings" @@ -65,6 +66,14 @@ const ( poolCrushRuleset ) +const ( + KiB uint64 = 1024 + MiB uint64 = 1024 * KiB + GiB uint64 = 1024 * MiB + TiB uint64 = 1024 * GiB + PiB uint64 = 1024 * TiB +) + type CephConfig struct { ConfigFile string `yaml:"configFile,omitempty"` Pool map[string]PoolProperties `yaml:"pool,flow"` @@ -454,29 +463,91 @@ func (d *Driver) DeleteSnapshot(opt *pb.DeleteVolumeSnapshotOpts) error { return err } -func (d *Driver) parseCapStr(cap string) int64 { - if cap == "0" { +func (d *Driver) parseCapStr(cap string) float64 { + rValue, _ := regexp.Compile("[0-9.]+") + rUnit, _ := regexp.Compile("[A-Za-z]+") + valueIndex := rValue.FindStringIndex(cap) + unitIndex := rUnit.FindStringIndex(cap) + log.V(5).Infof("cap = %v, valueIndex = %v, unitIndex = %v", cap, valueIndex, unitIndex) + + if (2 != len(valueIndex)) || (2 != len(unitIndex)) || (valueIndex[1] != unitIndex[0]) { + log.Error("Cap format is wrong") return 0 } - UnitMapper := map[string]uint64{ - "K": 0, //shift bit - "M": 10, - "G": 20, - "T": 30, - "P": 40, + + valueStr := cap[valueIndex[0]:valueIndex[1]] + var value float64 + + if strings.Contains(valueStr, ".") { + valueFloat, err := strconv.ParseFloat(valueStr, 64) + if err != nil { + log.Errorf("strconv.ParseFloat failed: %v", err) + return 0 + } + + value = valueFloat + } else { + valueInt, err := strconv.ParseInt(valueStr, 10, 64) + if err != nil { + log.Errorf("strconv.ParseInt failed: %v", err) + return 0 + } + + value = float64(valueInt) } - unit := strings.ToUpper(cap[len(cap)-1:]) - num, err := strconv.ParseInt(cap[:len(cap)-1], 10, 64) - if err != nil { - log.Error("Cannot convert this number", err) - return 0 + + unitStr := strings.ToUpper(cap[unitIndex[0]:unitIndex[1]]) + oldUnitMapper := map[string]uint64{ + "K": KiB, + "M": MiB, + "G": GiB, + "T": TiB, + "P": PiB, } - if val, ok := UnitMapper[unit]; ok { - return num << val >> 20 //Convert to unit GB + newUnitMapper := map[string]uint64{ + "KIB": KiB, + "MIB": MiB, + "GIB": GiB, + "TIB": TiB, + "PIB": PiB, + } + var unitInt uint64 + + if val, ok := newUnitMapper[unitStr]; ok { + unitInt = val } else { - log.Error("strage unit is not found.") - return 0 + if val, ok := oldUnitMapper[unitStr]; ok { + unitInt = val + } else { + log.Errorf("strage unitStr = %v is not found!", unitStr) + return 0 + } } + + var result float64 + switch unitInt { + case KiB: + result = value / float64(MiB) + break + case MiB: + result = value / float64(KiB) + break + case GiB: + result = value + break + case TiB: + result = value * float64(KiB) + break + case PiB: + result = value * float64(MiB) + break + default: + log.Errorf("strage unitStr = %v is not found!", unitStr) + break + } + + log.V(5).Infof("parseCapStr result = %v", result) + return result } func (d *Driver) getPoolsCapInfo() ([][]string, error) { @@ -580,6 +651,11 @@ func (d *Driver) ListPools() ([]*model.StoragePoolSpec, error) { totalCap := d.parseCapStr(gc[globalSize]) maxAvailCap := d.parseCapStr(pc[i][poolMaxAvail]) availCap := d.parseCapStr(gc[globalAvail]) + log.Infof("totalCap = %v, availCap = %v, maxAvailCap=%v", totalCap, availCap, maxAvailCap) + if 0 == availCap { + return nil, errors.New("ceph GLOBAL AVAIL = 0") + } + pol := &model.StoragePoolSpec{ BaseModel: &model.BaseModel{ Id: uuid.NewV5(uuid.NamespaceOID, name).String(), @@ -587,8 +663,8 @@ func (d *Driver) ListPools() ([]*model.StoragePoolSpec, error) { Name: name, //if redundancy type is replicate, MAX AVAIL = AVAIL / replicate number, //and if it is erasure, MAX AVAIL = AVAIL * k / (m + k) - TotalCapacity: totalCap * maxAvailCap / availCap, - FreeCapacity: maxAvailCap, + TotalCapacity: int64(totalCap * maxAvailCap / availCap), + FreeCapacity: int64(maxAvailCap), StorageType: c.Pool[name].StorageType, Extras: extras, AvailabilityZone: c.Pool[name].AvailabilityZone, From c92682d78aec2a149e7370fc0b056bf85f6fdf2c Mon Sep 17 00:00:00 2001 From: wisererik Date: Thu, 6 Dec 2018 19:48:39 +0800 Subject: [PATCH 5/7] Fix some bugs when filtering pool --- pkg/controller/selector/filter.go | 2 ++ pkg/model/base.go | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/controller/selector/filter.go b/pkg/controller/selector/filter.go index 5ff69e22b..84a54b141 100644 --- a/pkg/controller/selector/filter.go +++ b/pkg/controller/selector/filter.go @@ -201,6 +201,8 @@ func CompareOperator(op string, key string, reqValue string, value interface{}) // StringCompare ... func StringCompare(op string, key string, a string, b string) (bool, error) { + a = strings.ToLower(a) + b = strings.ToLower(b) switch op { case "s==", "": return a == b, nil diff --git a/pkg/model/base.go b/pkg/model/base.go index fb69af993..db8b47eea 100755 --- a/pkg/model/base.go +++ b/pkg/model/base.go @@ -53,7 +53,7 @@ type DataStorageLoS struct { // IsSpaceEfficient indicates that the storage is compressed or deduplicated. // The default value for this prperty is false. - IsSpaceEfficient bool `json:"isSpaceEfficient,omitempty" yaml:"isSpaceEfficient,omitempty"` + IsSpaceEfficient bool `json:"isSpaceEfficient" yaml:"isSpaceEfficient,omitempty"` } func (ds DataStorageLoS) IsEmpty() bool { @@ -95,7 +95,7 @@ func (ic IOConnectivityLoS) IsEmpty() bool { // capability are part of the advertising system. type DataProtectionLoS struct { // IsIsolated shall indicate if the replica is in a separate fault domain. - IsIsolated bool `json:"isIsolated,omitempty" yaml:"isIsolated,omitempty"` + IsIsolated bool `json:"isIsolated" yaml:"isIsolated,omitempty"` // MinLifetime shall be an ISO 8601 duration that specifies the minimum // required lifetime of the replica. For example, "P3Y6M4DT12H30M5S" From c11b7b810eb3dd5215b71f161c4bc94387f77143 Mon Sep 17 00:00:00 2001 From: Leon Wang Date: Fri, 7 Dec 2018 17:10:01 +0800 Subject: [PATCH 6/7] Update policy.json --- examples/policy.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/policy.json b/examples/policy.json index 337b6799c..b840b5bd2 100644 --- a/examples/policy.json +++ b/examples/policy.json @@ -9,9 +9,9 @@ "profile:get":"", "profile:update":"rule:admin_api", "profile:delete":"rule:admin_api", - "profile:add_extra_property": "rule:admin_api", - "profile:list_extra_properties": "", - "profile:remove_extra_property": "rule:admin_api", + "profile:add_custom_property": "rule:admin_api", + "profile:list_custom_properties": "", + "profile:remove_custom_property": "rule:admin_api", "volume:create": "rule:admin_or_owner", "volume:list": "rule:admin_or_owner", "volume:get": "rule:admin_or_owner", @@ -47,4 +47,4 @@ "volume_group:update": "rule:admin_or_owner", "volume_group:delete": "rule:admin_or_owner", "availability_zone:list":"" -} \ No newline at end of file +} From 89692ab11342db09a9ce31faea3b97242684d3f9 Mon Sep 17 00:00:00 2001 From: jerry Date: Fri, 7 Dec 2018 09:30:40 +0000 Subject: [PATCH 7/7] add keystone auth for multi-cloud and add retry mechanism --- contrib/backup/multicloud/client.go | 96 +++++++++++++++---- contrib/backup/multicloud/driver.go | 34 +++++-- contrib/backup/multicloud/driver_test.go | 11 ++- .../multicloud/testdata/multi-cloud.yaml | 10 +- 4 files changed, 120 insertions(+), 31 deletions(-) diff --git a/contrib/backup/multicloud/client.go b/contrib/backup/multicloud/client.go index fb2c5520d..36838489e 100644 --- a/contrib/backup/multicloud/client.go +++ b/contrib/backup/multicloud/client.go @@ -17,6 +17,7 @@ package multicloud import ( "encoding/xml" "fmt" + "io/ioutil" "net/http" "net/url" "path" @@ -25,7 +26,9 @@ import ( "github.com/astaxie/beego/httplib" log "github.com/golang/glog" - "io/ioutil" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" ) const ( @@ -35,49 +38,106 @@ const ( ApiVersion = "v1" ) -type AuthOptions struct { - Endpoint string - UserName string - Password string - TenantId string -} - type Client struct { endpoint string - userName string - password string tenantId string version string baseURL string + auth *AuthOptions + token *tokens.Token timeout time.Duration uploadTimeout time.Duration } -func NewClient(opt *AuthOptions, uploadTimeout int64) (*Client, error) { - u, err := url.Parse(opt.Endpoint) +func NewClient(endpooint string, opt *AuthOptions, uploadTimeout int64) (*Client, error) { + u, err := url.Parse(endpooint) if err != nil { return nil, err } u.Path = path.Join(u.Path, ApiVersion) baseURL := u.String() + "/" - return &Client{ - endpoint: opt.Endpoint, - userName: opt.UserName, - password: opt.Password, - tenantId: opt.TenantId, + client := &Client{ + endpoint: endpooint, + tenantId: DefaultTenantId, version: ApiVersion, baseURL: baseURL, timeout: time.Duration(DefaultTimeout) * time.Minute, uploadTimeout: time.Duration(uploadTimeout) * time.Minute, - }, nil + auth: opt, + } + + if opt.Strategy == "keystone" { + if err := client.UpdateToken(); err != nil { + return nil, err + } + } + return client, nil } type ReqSettingCB func(req *httplib.BeegoHTTPRequest) error +func (c *Client) getToken(opt *AuthOptions) (*tokens.CreateResult, error) { + auth := gophercloud.AuthOptions{ + IdentityEndpoint: opt.AuthUrl, + DomainName: opt.DomainName, + Username: opt.UserName, + Password: opt.Password, + TenantName: opt.TenantName, + } + + provider, err := openstack.AuthenticatedClient(auth) + if err != nil { + log.Error("When get auth client:", err) + return nil, err + } + + // Only support keystone v3 + identity, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) + if err != nil { + log.Error("When get identity session:", err) + return nil, err + } + r := tokens.Create(identity, &auth) + return &r, nil +} + +func (c *Client) UpdateToken() error { + t, err := c.getToken(c.auth) + if err != nil { + log.Errorf("Get token failed, %v", err) + return err + } + project, err := t.ExtractProject() + if err != nil { + log.Errorf("extract project failed, %v", err) + return err + } + c.tenantId = project.ID + token, err := t.ExtractToken() + if err != nil { + log.Errorf("extract token failed, %v", err) + return err + } + c.token = token + log.V(5).Infof("TokenId:%s, ExpiresAt:%v", token.ID, token.ExpiresAt) + return nil +} + func (c *Client) doRequest(method, u string, in interface{}, cb ReqSettingCB) ([]byte, http.Header, error) { req := httplib.NewBeegoRequest(u, method) req.Header("Content-Type", "application/xml") + if c.auth.Strategy == "keystone" { + beforeExpires := c.token.ExpiresAt.Add(time.Minute) + if time.Now().After(beforeExpires) { + log.Warning("token is about to expire, update it") + if err := c.UpdateToken(); err != nil { + return nil, nil, err + } + } + req.Header("X-Auth-Token", c.token.ID) + } + req.SetTimeout(c.timeout, c.timeout) if cb != nil { if err := cb(req); err != nil { diff --git a/contrib/backup/multicloud/driver.go b/contrib/backup/multicloud/driver.go index b6394142f..f294e98d2 100644 --- a/contrib/backup/multicloud/driver.go +++ b/contrib/backup/multicloud/driver.go @@ -22,6 +22,7 @@ import ( "github.com/golang/glog" "github.com/opensds/opensds/contrib/backup" + "github.com/opensds/opensds/pkg/utils" "gopkg.in/yaml.v2" ) @@ -38,11 +39,21 @@ func NewMultiCloud() (backup.BackupDriver, error) { return &MultiCloud{}, nil } +type AuthOptions struct { + Strategy string `yaml:"Strategy"` + AuthUrl string `yaml:"AuthUrl,omitempty"` + DomainName string `yaml:"DomainName,omitempty"` + UserName string `yaml:"UserName,omitempty"` + Password string `yaml:"Password,omitempty"` + TenantName string `yaml:"TenantName,omitempty"` +} + type MultiCloudConf struct { Endpoint string `yaml:"Endpoint,omitempty"` - TenantId string `yaml:"TenantId,omitempty"` UploadTimeout int64 `yaml:"UploadTimeout,omitempty"` + AuthOptions `yaml:"AuthOptions,omitempty"` } + type MultiCloud struct { client *Client conf *MultiCloudConf @@ -51,7 +62,6 @@ type MultiCloud struct { func (m *MultiCloud) loadConf(p string) (*MultiCloudConf, error) { conf := &MultiCloudConf{ Endpoint: "http://127.0.0.1:8088", - TenantId: DefaultTenantId, UploadTimeout: DefaultUploadTimeout, } confYaml, err := ioutil.ReadFile(p) @@ -73,11 +83,7 @@ func (m *MultiCloud) SetUp() error { return err } - opt := &AuthOptions{ - Endpoint: m.conf.Endpoint, - TenantId: m.conf.TenantId, - } - if m.client, err = NewClient(opt, m.conf.UploadTimeout); err != nil { + if m.client, err = NewClient(m.conf.Endpoint, &m.conf.AuthOptions, m.conf.UploadTimeout); err != nil { return err } @@ -118,7 +124,12 @@ func (m *MultiCloud) Backup(backup *backup.BackupSpec, volFile *os.File) error { if size == 0 { break } - uploadResp, err := m.client.UploadPart(bucket, key, partNum, initResp.UploadId, buf[:size], int64(size)) + var uploadResp *UploadPartResult + err = utils.Retry(3, "upload part", false, func(retryIdx int, lastErr error) error { + var inErr error + uploadResp, inErr = m.client.UploadPart(bucket, key, partNum, initResp.UploadId, buf[:size], int64(size)) + return inErr + }) if err != nil { glog.Errorf("upload part failed, err:%v", err) return err @@ -145,7 +156,12 @@ func (m *MultiCloud) Restore(backup *backup.BackupSpec, backupId string, volFile // if the size of data of smaller than require download size // downloading is completed. for offset := int64(0); downloadSize == ChunkSize; offset += ChunkSize { - data, err := m.client.DownloadPart(bucket, backupId, offset, ChunkSize) + var data []byte + err := utils.Retry(3, "download part", false, func(retryIdx int, lastErr error) error { + var inErr error + data, inErr = m.client.DownloadPart(bucket, backupId, offset, ChunkSize) + return inErr + }) if err != nil { glog.Errorf("download part failed: %v", err) return err diff --git a/contrib/backup/multicloud/driver_test.go b/contrib/backup/multicloud/driver_test.go index 03b9e58fc..9faea46f3 100644 --- a/contrib/backup/multicloud/driver_test.go +++ b/contrib/backup/multicloud/driver_test.go @@ -32,10 +32,17 @@ func TestLoadConf(t *testing.T) { } expect := &MultiCloudConf{ Endpoint: "http://127.0.0.1:8088", - TenantId: "FakeTenantId", UploadTimeout: DefaultUploadTimeout, + AuthOptions: AuthOptions{ + Strategy: "keystone", + AuthUrl: "http://127.0.0.1/identity", + DomainName: "Default", + UserName: "admin", + Password: "opensds@123", + TenantName: "admin", + }, } - fmt.Println(conf) + fmt.Printf("%+v", conf) if !reflect.DeepEqual(expect, conf) { t.Errorf("load conf file error") } diff --git a/contrib/backup/multicloud/testdata/multi-cloud.yaml b/contrib/backup/multicloud/testdata/multi-cloud.yaml index 65bbc8ede..63d4a2de2 100644 --- a/contrib/backup/multicloud/testdata/multi-cloud.yaml +++ b/contrib/backup/multicloud/testdata/multi-cloud.yaml @@ -1,4 +1,10 @@ "Endpoint": "http://127.0.0.1:8088" -"TenantId": "FakeTenantId" # upload object timeout in seconds -"UploadTimeout": 30 \ No newline at end of file +"UploadTimeout": 30 +AuthOptions: + Strategy: "keystone" + AuthUrl: "http://127.0.0.1/identity" + DomainName: "Default" + UserName: "admin" + Password: "opensds@123" + TenantName: "admin" \ No newline at end of file