Added Capability to get volumes and volumes with details
Partially implements blueprint volume Change-Id: Ie7d6f0c6e137499e5837cf0a94ca612bc83512a4
This commit is contained in:
parent
784256a823
commit
3a6d72d71b
79
examples/40-volume-v2.go
Normal file
79
examples/40-volume-v2.go
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
//
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.openstack.org/openstack/golang-client.git/volume"
|
||||
"git.openstack.org/openstack/golang-client.git/openstack"
|
||||
)
|
||||
|
||||
// Volume examples.
|
||||
func main() {
|
||||
config := getConfig()
|
||||
|
||||
// Authenticate with a username, password, tenant id.
|
||||
creds := openstack.AuthOpts{
|
||||
AuthUrl: config.Host,
|
||||
Project: config.ProjectName,
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
}
|
||||
auth, err := openstack.DoAuthRequest(creds)
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("There was an error authenticating:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
if !auth.GetExpiration().After(time.Now()) {
|
||||
panic("There was an error. The auth token has an invalid expiration.")
|
||||
}
|
||||
|
||||
// Find the endpoint for the volume v2 service.
|
||||
url, err := auth.GetEndpoint("volumev2", "")
|
||||
if url == "" || err != nil {
|
||||
panic("v2 volume service url not found during authentication")
|
||||
}
|
||||
|
||||
// Make a new client with these creds
|
||||
sess, err := openstack.NewSession(nil, auth, nil)
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("Error crating new Session:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
|
||||
volumeService := volume.Service{
|
||||
Session: *sess,
|
||||
Client: *http.DefaultClient,
|
||||
URL: url, // We're forcing Volume v2 for now
|
||||
}
|
||||
volumesDetails, err := volumeService.VolumesDetail()
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("Cannot access volumes:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
|
||||
var volumeIDs = make([]string, 0)
|
||||
for _, element := range volumesDetails {
|
||||
volumeIDs = append(volumeIDs, element.ID)
|
||||
}
|
||||
|
||||
if len(volumeIDs) == 0 {
|
||||
panicString := fmt.Sprint("No volumes found, check to make sure access is correct")
|
||||
panic(panicString)
|
||||
}
|
||||
}
|
210
volume/volume.go
Normal file
210
volume/volume.go
Normal file
@ -0,0 +1,210 @@
|
||||
// Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/*
|
||||
Package volume implements a client library for accessing OpenStack Volume service
|
||||
|
||||
Volumes and VolumeDetails can be retrieved using the api.
|
||||
|
||||
In addition more complex filtering and sort queries can by using the VolumeQueryParameters.
|
||||
|
||||
*/
|
||||
package volume
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"git.openstack.org/openstack/golang-client.git/openstack"
|
||||
"git.openstack.org/openstack/golang-client.git/util"
|
||||
)
|
||||
|
||||
// Service is a client service that can make
|
||||
// requests against a OpenStack volume service.
|
||||
// Below is an example on creating an volume service and getting volumes:
|
||||
// volumeService := volume.VolumeService{Client: *http.DefaultClient, TokenId: tokenId, Url: "http://volumeservicelocation"}
|
||||
// volumes:= volumeService.Volumes()
|
||||
type Service struct {
|
||||
Session openstack.Session
|
||||
Client http.Client
|
||||
URL string
|
||||
}
|
||||
|
||||
// Response is a structure for all properties of
|
||||
// an volume for a non detailed query
|
||||
type Response struct {
|
||||
ID string `json:"id"`
|
||||
Links []map[string]string `json:"links"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// DetailResponse is a structure for all properties of
|
||||
// an volume for a detailed query
|
||||
type DetailResponse struct {
|
||||
ID string `json:"id"`
|
||||
Attachments []map[string]string `json:"attachments"`
|
||||
Links []map[string]string `json:"links"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
Protected bool `json:"protected"`
|
||||
Status string `json:"status"`
|
||||
MigrationStatus string `json:"migration_status"`
|
||||
UserID string `json:"user_id"`
|
||||
Encrypted bool `json:"encrypted"`
|
||||
Multiattach bool `json:"multiattach"`
|
||||
CreatedAt util.RFC8601DateTime `json:"created_at"`
|
||||
Description string `json:"description"`
|
||||
Volume_type string `json:"volume_type"`
|
||||
Name string `json:"name"`
|
||||
Source_volid string `json:"source_volid"`
|
||||
Snapshot_id string `json:"snapshot_id"`
|
||||
Size int64 `json:"size"`
|
||||
|
||||
Aavailability_zone string `json:"availability_zone"`
|
||||
Rreplication_status string `json:"replication_status"`
|
||||
Consistencygroup_id string `json:"consistencygroup_id"`
|
||||
}
|
||||
|
||||
// QueryParameters is a structure that
|
||||
// contains the filter, sort, and paging parameters for
|
||||
// an volume or volumedetail query.
|
||||
type QueryParameters struct {
|
||||
All_tenant int64
|
||||
Marker string
|
||||
Limit int64
|
||||
SortKey string
|
||||
SortDirection SortDirection
|
||||
}
|
||||
|
||||
// SortDirection of the sort, ascending or descending.
|
||||
type SortDirection string
|
||||
|
||||
const (
|
||||
// Desc specifies the sort direction to be descending.
|
||||
Desc SortDirection = "desc"
|
||||
// Asc specifies the sort direction to be ascending.
|
||||
Asc SortDirection = "asc"
|
||||
)
|
||||
|
||||
// Volumes will issue a get request to OpenStack to retrieve the list of volumes.
|
||||
func (volumeService Service) Volumes() (volume []Response, err error) {
|
||||
return volumeService.QueryVolumes(nil)
|
||||
}
|
||||
|
||||
// VolumesDetail will issue a get request to OpenStack to retrieve the list of volumes complete with
|
||||
// additional details.
|
||||
func (volumeService Service) VolumesDetail() (volume []DetailResponse, err error) {
|
||||
return volumeService.QueryVolumesDetail(nil)
|
||||
}
|
||||
|
||||
// QueryVolumes will issue a get request with the specified VolumeQueryParameters to retrieve the list of
|
||||
// volumes.
|
||||
func (volumeService Service) QueryVolumes(queryParameters *QueryParameters) ([]Response, error) {
|
||||
volumesContainer := volumesResponse{}
|
||||
err := volumeService.queryVolumes(false /*includeDetails*/, &volumesContainer, queryParameters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return volumesContainer.Volumes, nil
|
||||
}
|
||||
|
||||
// QueryVolumesDetail will issue a get request with the specified QueryParameters to retrieve the list of
|
||||
// volumes with additional details.
|
||||
func (volumeService Service) QueryVolumesDetail(queryParameters *QueryParameters) ([]DetailResponse, error) {
|
||||
volumesDetailContainer := volumesDetailResponse{}
|
||||
err := volumeService.queryVolumes(true /*includeDetails*/, &volumesDetailContainer, queryParameters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return volumesDetailContainer.Volumes, nil
|
||||
}
|
||||
|
||||
func (volumeService Service) queryVolumes(includeDetails bool, volumesResponseContainer interface{}, queryParameters *QueryParameters) error {
|
||||
urlPostFix := "/volumes"
|
||||
if includeDetails {
|
||||
urlPostFix = urlPostFix + "/detail"
|
||||
}
|
||||
|
||||
reqURL, err := buildQueryURL(volumeService, queryParameters, urlPostFix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var headers http.Header = http.Header{}
|
||||
headers.Set("Accept", "application/json")
|
||||
resp, err := volumeService.Session.Get(reqURL.String(), nil, &headers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = util.CheckHTTPResponseStatusCode(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rbody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return errors.New("aaa")
|
||||
}
|
||||
if err = json.Unmarshal(rbody, &volumesResponseContainer); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildQueryURL(volumeService Service, queryParameters *QueryParameters, volumePartialURL string) (*url.URL, error) {
|
||||
reqURL, err := url.Parse(volumeService.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if queryParameters != nil {
|
||||
values := url.Values{}
|
||||
if queryParameters.All_tenant != 0 {
|
||||
values.Set("all_tenant", fmt.Sprintf("%d", queryParameters.All_tenant))
|
||||
}
|
||||
if queryParameters.Limit != 0 {
|
||||
values.Set("limit", fmt.Sprintf("%d", queryParameters.Limit))
|
||||
}
|
||||
if queryParameters.Marker != "" {
|
||||
values.Set("marker", queryParameters.Marker)
|
||||
}
|
||||
if queryParameters.SortKey != "" {
|
||||
values.Set("sort_key", queryParameters.SortKey)
|
||||
}
|
||||
if queryParameters.SortDirection != "" {
|
||||
values.Set("sort_dir", string(queryParameters.SortDirection))
|
||||
}
|
||||
|
||||
if len(values) > 0 {
|
||||
reqURL.RawQuery = values.Encode()
|
||||
}
|
||||
}
|
||||
reqURL.Path += volumePartialURL
|
||||
|
||||
return reqURL, nil
|
||||
}
|
||||
|
||||
type volumesDetailResponse struct {
|
||||
Volumes []DetailResponse `json:"volumes"`
|
||||
}
|
||||
|
||||
type volumesResponse struct {
|
||||
Volumes []Response `json:"volumes"`
|
||||
}
|
220
volume/volume_test.go
Normal file
220
volume/volume_test.go
Normal file
@ -0,0 +1,220 @@
|
||||
// Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
|
||||
// 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.
|
||||
|
||||
// volume.go
|
||||
package volume_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.openstack.org/openstack/golang-client.git/volume/v2"
|
||||
"git.openstack.org/openstack/golang-client.git/openstack"
|
||||
"git.openstack.org/openstack/golang-client.git/testUtil"
|
||||
"git.openstack.org/openstack/golang-client.git/util"
|
||||
)
|
||||
|
||||
var tokn = "ae5aebe5-6a5d-4a40-840a-9736a067aff4"
|
||||
|
||||
func TestListVolumes(t *testing.T) {
|
||||
anon := func(volumeService *volume.Service) {
|
||||
volumes, err := volumeService.Volumes()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(volumes) != 2 {
|
||||
t.Error(errors.New("Incorrect number of volumes found"))
|
||||
}
|
||||
expectedVolume := volume.Response{
|
||||
Name: "volume_test1",
|
||||
ID: "f5fc9874-fc89-4814-a358-23ba83a6115f",
|
||||
Links: []map[string]string{{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "self"},
|
||||
{"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "bookmark"}}}
|
||||
// Verify first one matches expected values
|
||||
testUtil.Equals(t, expectedVolume, volumes[0])
|
||||
}
|
||||
|
||||
testVolumeServiceAction(t, "volumes", sampleVolumesData, anon)
|
||||
}
|
||||
|
||||
func TestListVolumeDetails(t *testing.T) {
|
||||
anon := func(volumeService *volume.Service) {
|
||||
volumes, err := volumeService.VolumesDetail()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(volumes) != 2 {
|
||||
t.Error(errors.New("Incorrect number of volumes found"))
|
||||
}
|
||||
createdAt, _ := util.NewDateTime(`"2014-09-29T14:44:31"`)
|
||||
expectedVolumeDetail := volume.DetailResponse{
|
||||
ID: "30becf77-63fe-4f5e-9507-a0578ffe0949",
|
||||
Attachments: []map[string]string{{"attachment_id": "ddb2ac07-ed62-49eb-93da-73b258dd9bec", "host_name": "host_test", "volume_id": "30becf77-63fe-4f5e-9507-a0578ffe0949", "device": "/dev/vdb", "id": "30becf77-63fe-4f5e-9507-a0578ffe0949", "server_id": "0f081aae-1b0c-4b89-930c-5f2562460c72"}},
|
||||
Links: []map[string]string{{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/30becf77-63fe-4f5e-9507-a0578ffe0949", "rel": "self"},
|
||||
{"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/30becf77-63fe-4f5e-9507-a0578ffe0949", "rel": "bookmark"}},
|
||||
Metadata: map[string]string{"readonly": "false", "attached_mode": "rw"},
|
||||
Protected: false,
|
||||
Status: "available",
|
||||
MigrationStatus: "",
|
||||
UserID: "a971aa69-c61a-4a49-b392-b0e41609bc5d",
|
||||
Encrypted: false,
|
||||
Multiattach: false,
|
||||
CreatedAt: createdAt,
|
||||
Description: "test volume",
|
||||
Volume_type: "test_type",
|
||||
Name: "test_volume",
|
||||
Source_volid: "4b58bbb8-3b00-4f87-8243-8c622707bbab",
|
||||
Snapshot_id: "cc488e4a-9649-4e5f-ad12-20ab37c683b5",
|
||||
Size: 2,
|
||||
|
||||
Aavailability_zone: "default_cluster",
|
||||
Rreplication_status: "",
|
||||
Consistencygroup_id: ""}
|
||||
testUtil.Equals(t, expectedVolumeDetail, volumes[0])
|
||||
}
|
||||
|
||||
testVolumeServiceAction(t, "volumes/detail", sampleVolumeDetailsData, anon)
|
||||
}
|
||||
|
||||
func TestLimitFilterUrlProduced(t *testing.T) {
|
||||
testVolumeQueryParameter(t, "volumes?limit=2",
|
||||
volume.QueryParameters{Limit: 2})
|
||||
}
|
||||
|
||||
func TestAll_tenantFilterUrlProduced(t *testing.T) {
|
||||
testVolumeQueryParameter(t, "volumes?all_tenant=1",
|
||||
volume.QueryParameters{All_tenant: 1})
|
||||
}
|
||||
|
||||
func TestMarkerUrlProduced(t *testing.T) {
|
||||
testVolumeQueryParameter(t, "volumes?marker=1776335d-72f1-48c9-b0e7-74c62cb8fede",
|
||||
volume.QueryParameters{Marker: "1776335d-72f1-48c9-b0e7-74c62cb8fede"})
|
||||
}
|
||||
|
||||
func TestSortKeySortUrlProduced(t *testing.T) {
|
||||
testVolumeQueryParameter(t, "volumes?sort_key=id",
|
||||
volume.QueryParameters{SortKey: "id"})
|
||||
}
|
||||
|
||||
func TestSortDirSortUrlProduced(t *testing.T) {
|
||||
testVolumeQueryParameter(t, "volumes?sort_dir=asc",
|
||||
volume.QueryParameters{SortDirection: volume.Asc})
|
||||
}
|
||||
|
||||
func testVolumeQueryParameter(t *testing.T, uriEndsWith string, queryParameters volume.QueryParameters) {
|
||||
anon := func(volumeService *volume.Service) {
|
||||
_, _ = volumeService.QueryVolumes(&queryParameters)
|
||||
}
|
||||
|
||||
testVolumeServiceAction(t, uriEndsWith, sampleVolumesData, anon)
|
||||
}
|
||||
|
||||
func testVolumeServiceAction(t *testing.T, uriEndsWith string, testData string, volumeServiceAction func(*volume.Service)) {
|
||||
anon := func(req *http.Request) {
|
||||
reqURL := req.URL.String()
|
||||
if !strings.HasSuffix(reqURL, uriEndsWith) {
|
||||
t.Error(errors.New("Incorrect url created, expected:" + uriEndsWith + " at the end, actual url:" + reqURL))
|
||||
}
|
||||
}
|
||||
apiServer := testUtil.CreateGetJSONTestRequestServer(t, tokn, testData, anon)
|
||||
defer apiServer.Close()
|
||||
|
||||
auth := openstack.AuthToken{
|
||||
Access: openstack.AccessType{
|
||||
Token: openstack.Token{
|
||||
ID: tokn,
|
||||
},
|
||||
},
|
||||
}
|
||||
sess, _ := openstack.NewSession(http.DefaultClient, auth, nil)
|
||||
volumeService := volume.Service{
|
||||
Session: *sess,
|
||||
URL: apiServer.URL,
|
||||
}
|
||||
volumeServiceAction(&volumeService)
|
||||
}
|
||||
|
||||
var sampleVolumesData = `{
|
||||
"volumes":[
|
||||
{
|
||||
"name":"volume_test1",
|
||||
"id":"f5fc9874-fc89-4814-a358-23ba83a6115f",
|
||||
"links":[{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "self"},
|
||||
{"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/f5fc9874-fc89-4814-a358-23ba83a6115f", "rel": "bookmark"}]
|
||||
},
|
||||
{
|
||||
"name":"volume_test1",
|
||||
"id":"60055a0a-2451-4d78-af9c-f2302150602f",
|
||||
"links":[{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/60055a0a-2451-4d78-af9c-f2302150602f", "rel": "self"},
|
||||
{"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/60055a0a-2451-4d78-af9c-f2302150602f", "rel": "bookmark"}]
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
var sampleVolumeDetailsData = `{
|
||||
"volumes":[
|
||||
{
|
||||
"id":"30becf77-63fe-4f5e-9507-a0578ffe0949",
|
||||
"attachments":[{"attachment_id": "ddb2ac07-ed62-49eb-93da-73b258dd9bec", "host_name": "host_test", "volume_id": "30becf77-63fe-4f5e-9507-a0578ffe0949", "device": "/dev/vdb", "id": "30becf77-63fe-4f5e-9507-a0578ffe0949", "server_id": "0f081aae-1b0c-4b89-930c-5f2562460c72"}],
|
||||
"links":[{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/30becf77-63fe-4f5e-9507-a0578ffe0949", "rel": "self"},
|
||||
{"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/30becf77-63fe-4f5e-9507-a0578ffe0949", "rel": "bookmark"}],
|
||||
"metadata":{"readonly": "false", "attached_mode": "rw"},
|
||||
"protected":false,
|
||||
"status":"available",
|
||||
"migrationStatus":null,
|
||||
"user_id":"a971aa69-c61a-4a49-b392-b0e41609bc5d",
|
||||
"encrypted":false,
|
||||
"multiattach":false,
|
||||
"created_at":"2014-09-29T14:44:31",
|
||||
"description":"test volume",
|
||||
"volume_type":"test_type",
|
||||
"name":"test_volume",
|
||||
"source_volid":"4b58bbb8-3b00-4f87-8243-8c622707bbab",
|
||||
"snapshot_id":"cc488e4a-9649-4e5f-ad12-20ab37c683b5",
|
||||
"size":2,
|
||||
|
||||
"availability_zone":"default_cluster",
|
||||
"replication_status":null,
|
||||
"consistencygroup_id":null
|
||||
},
|
||||
{
|
||||
"id":"242d3d14-2efd-4c63-9a6b-ef6bc8eed756",
|
||||
"attachments":[{"attachment_id": "9d4fb045-f957-489b-9e7d-f6f156002c04", "host_name": "host_test2", "volume_id": "242d3d14-2efd-4c63-9a6b-ef6bc8eed756", "device": "/dev/vdb", "id": "242d3d14-2efd-4c63-9a6b-ef6bc8eed756", "server_id": "9f47bd1c-c596-424d-abbe-e5e1a7a65fdc"}],
|
||||
"links":[{"href": "http://172.16.197.131:8776/v2/1d8837c5fcef4892951397df97661f97/volumes/242d3d14-2efd-4c63-9a6b-ef6bc8eed756", "rel": "self"},
|
||||
{"href": "http://172.16.197.131:8776/1d8837c5fcef4892951397df97661f97/volumes/242d3d14-2efd-4c63-9a6b-ef6bc8eed756", "rel": "bookmark"}],
|
||||
"metadata":{"readonly": "false", "attached_mode": "rw"},
|
||||
"protected":false,
|
||||
"status":"available",
|
||||
"migrationStatus":null,
|
||||
"user_id":"a971aa69-c61a-4a49-b392-b0e41609bc5d",
|
||||
"encrypted":false,
|
||||
"multiattach":false,
|
||||
"created_at":"2014-09-29T14:44:35",
|
||||
"description":"test volume 2",
|
||||
"volume_type":"test_type",
|
||||
"name":"test_volume2",
|
||||
"source_volid":null,
|
||||
"snapshot_id":null,
|
||||
"size":2,
|
||||
|
||||
"availability_zone":"default_cluster",
|
||||
"replication_status":null,
|
||||
"consistencygroup_id":null
|
||||
}
|
||||
]
|
||||
}`
|
Loading…
x
Reference in New Issue
Block a user