Merge "Enable volumes metadata update"
This commit is contained in:
commit
1d14504de9
@ -355,6 +355,14 @@ def volume_update(request, volume_id, name, description):
|
||||
**vol_data)
|
||||
|
||||
|
||||
def volume_set_metadata(request, volume_id, metadata):
|
||||
return cinderclient(request).volumes.set_metadata(volume_id, metadata)
|
||||
|
||||
|
||||
def volume_delete_metadata(request, volume_id, keys):
|
||||
return cinderclient(request).volumes.delete_metadata(volume_id, keys)
|
||||
|
||||
|
||||
def volume_reset_state(request, volume_id, state):
|
||||
return cinderclient(request).volumes.reset_state(volume_id, state)
|
||||
|
||||
@ -445,6 +453,16 @@ def volume_snapshot_update(request, snapshot_id, name, description):
|
||||
**snapshot_data)
|
||||
|
||||
|
||||
def volume_snapshot_set_metadata(request, snapshot_id, metadata):
|
||||
return cinderclient(request).volume_snapshots.set_metadata(
|
||||
snapshot_id, metadata)
|
||||
|
||||
|
||||
def volume_snapshot_delete_metadata(request, snapshot_id, keys):
|
||||
return cinderclient(request).volume_snapshots.delete_metadata(
|
||||
snapshot_id, keys)
|
||||
|
||||
|
||||
def volume_snapshot_reset_state(request, snapshot_id, state):
|
||||
return cinderclient(request).volume_snapshots.reset_state(
|
||||
snapshot_id, state)
|
||||
@ -792,7 +810,7 @@ def volume_type_extra_set(request, type_id, metadata):
|
||||
|
||||
def volume_type_extra_delete(request, type_id, keys):
|
||||
vol_type = volume_type_get(request, type_id)
|
||||
return vol_type.unset_keys([keys])
|
||||
return vol_type.unset_keys(keys)
|
||||
|
||||
|
||||
def qos_spec_list(request):
|
||||
|
@ -132,6 +132,34 @@ class VolumeTypes(generic.View):
|
||||
return {'items': [api.cinder.VolumeType(u).to_dict() for u in result]}
|
||||
|
||||
|
||||
@urls.register
|
||||
class VolumeMetadata(generic.View):
|
||||
"""API for volume metadata"""
|
||||
url_regex = r'cinder/volumes/(?P<volume_id>[^/]+)/metadata$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, volume_id):
|
||||
"""Get a specific volume's metadata
|
||||
|
||||
http://localhost/api/cinder/volumes/1/metadata
|
||||
"""
|
||||
return api.cinder.volume_get(request,
|
||||
volume_id).to_dict().get('metadata')
|
||||
|
||||
@rest_utils.ajax()
|
||||
def patch(self, request, volume_id):
|
||||
"""Update metadata items for specific volume
|
||||
|
||||
http://localhost/api/cinder/volumes/1/metadata
|
||||
"""
|
||||
updated = request.DATA['updated']
|
||||
removed = request.DATA['removed']
|
||||
if updated:
|
||||
api.cinder.volume_set_metadata(request, volume_id, updated)
|
||||
if removed:
|
||||
api.cinder.volume_delete_metadata(request, volume_id, removed)
|
||||
|
||||
|
||||
@urls.register
|
||||
class VolumeType(generic.View):
|
||||
"""API for getting a volume type.
|
||||
@ -179,6 +207,74 @@ class VolumeSnapshots(generic.View):
|
||||
return {'items': [u.to_dict() for u in result]}
|
||||
|
||||
|
||||
@urls.register
|
||||
class VolumeSnapshotMetadata(generic.View):
|
||||
"""API for getting snapshots metadata"""
|
||||
url_regex = r'cinder/volumesnapshots/' \
|
||||
r'(?P<volume_snapshot_id>[^/]+)/metadata$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, volume_snapshot_id):
|
||||
"""Get a specific volumes snapshot metadata
|
||||
|
||||
http://localhost/api/cinder/volumesnapshots/1/metadata
|
||||
"""
|
||||
result = api.cinder.volume_snapshot_get(request,
|
||||
volume_snapshot_id).\
|
||||
to_dict().get('metadata')
|
||||
return result
|
||||
|
||||
@rest_utils.ajax()
|
||||
def patch(self, request, volume_snapshot_id):
|
||||
"""Update metadata for specific volume snapshot
|
||||
|
||||
http://localhost/api/cinder/volumesnapshots/1/metadata
|
||||
"""
|
||||
updated = request.DATA['updated']
|
||||
removed = request.DATA['removed']
|
||||
if updated:
|
||||
api.cinder.volume_snapshot_set_metadata(request,
|
||||
volume_snapshot_id,
|
||||
updated)
|
||||
if removed:
|
||||
api.cinder.volume_snapshot_delete_metadata(request,
|
||||
volume_snapshot_id,
|
||||
removed)
|
||||
|
||||
|
||||
@urls.register
|
||||
class VolumeTypeMetadata(generic.View):
|
||||
"""API for getting snapshots metadata"""
|
||||
url_regex = r'cinder/volumetypes/(?P<volume_type_id>[^/]+)/metadata$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, volume_type_id):
|
||||
"""Get a specific volume's metadata
|
||||
|
||||
http://localhost/api/cinder/volumetypes/1/metadata
|
||||
"""
|
||||
metadata = api.cinder.volume_type_extra_get(request, volume_type_id)
|
||||
result = {x.key: x.value for x in metadata}
|
||||
return result
|
||||
|
||||
@rest_utils.ajax()
|
||||
def patch(self, request, volume_type_id):
|
||||
"""Update metadata for specific volume
|
||||
|
||||
http://localhost/api/cinder/volumetypes/1/metadata
|
||||
"""
|
||||
updated = request.DATA['updated']
|
||||
removed = request.DATA['removed']
|
||||
if updated:
|
||||
api.cinder.volume_type_extra_set(request,
|
||||
volume_type_id,
|
||||
updated)
|
||||
if removed:
|
||||
api.cinder.volume_type_extra_delete(request,
|
||||
volume_type_id,
|
||||
removed)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Extensions(generic.View):
|
||||
"""API for cinder extensions.
|
||||
|
@ -72,7 +72,8 @@ class VolumeSnapshotsTable(volumes_tables.VolumesTableBase):
|
||||
table_actions = (snapshots_tables.VolumeSnapshotsFilterAction,
|
||||
snapshots_tables.DeleteVolumeSnapshot,)
|
||||
row_actions = (snapshots_tables.DeleteVolumeSnapshot,
|
||||
UpdateVolumeSnapshotStatus,)
|
||||
UpdateVolumeSnapshotStatus,
|
||||
snapshots_tables.UpdateMetadata)
|
||||
row_class = UpdateRow
|
||||
status_columns = ("status",)
|
||||
columns = ('tenant', 'host', 'name', 'description', 'size', 'status',
|
||||
|
@ -19,7 +19,7 @@ from openstack_dashboard.test import helpers as test
|
||||
|
||||
from openstack_dashboard.dashboards.admin.volumes.snapshots import forms
|
||||
|
||||
INDEX_URL = reverse('horizon:admin:volumes:index')
|
||||
INDEX_URL = 'horizon:admin:volumes:index'
|
||||
|
||||
|
||||
class VolumeSnapshotsViewTests(test.BaseAdminViewTests):
|
||||
@ -79,7 +79,7 @@ class VolumeSnapshotsViewTests(test.BaseAdminViewTests):
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertMessageCount(error=1)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
self.assertRedirectsNoFollow(res, reverse(INDEX_URL))
|
||||
|
||||
@test.create_stubs({cinder: ('volume_snapshot_get',
|
||||
'volume_get')})
|
||||
@ -101,7 +101,7 @@ class VolumeSnapshotsViewTests(test.BaseAdminViewTests):
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertMessageCount(error=1)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
self.assertRedirectsNoFollow(res, reverse(INDEX_URL))
|
||||
|
||||
def test_get_snapshot_status_choices_without_current(self):
|
||||
current_status = {'status': 'available'}
|
||||
|
@ -195,6 +195,23 @@ class UpdateRow(tables.Row):
|
||||
return volume_type
|
||||
|
||||
|
||||
class UpdateMetadata(tables.LinkAction):
|
||||
name = "update_metadata"
|
||||
verbose_name = _("Update Metadata")
|
||||
ajax = False
|
||||
attrs = {"ng-controller": "MetadataModalHelperController as modal"}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs['preempt'] = True
|
||||
super(UpdateMetadata, self).__init__(**kwargs)
|
||||
|
||||
def get_link_url(self, datum):
|
||||
obj_id = self.table.get_object_id(datum)
|
||||
self.attrs['ng-click'] = (
|
||||
"modal.openMetadataModal('volume_type', '%s', true)" % obj_id)
|
||||
return "javascript:void(0);"
|
||||
|
||||
|
||||
class VolumeTypesTable(tables.DataTable):
|
||||
name = tables.WrappingColumn("name", verbose_name=_("Name"),
|
||||
form_field=forms.CharField(max_length=64))
|
||||
@ -233,7 +250,8 @@ class VolumeTypesTable(tables.DataTable):
|
||||
EditVolumeType,
|
||||
UpdateVolumeTypeEncryption,
|
||||
DeleteVolumeTypeEncryption,
|
||||
DeleteVolumeType,)
|
||||
DeleteVolumeType,
|
||||
UpdateMetadata)
|
||||
row_class = UpdateRow
|
||||
|
||||
|
||||
|
@ -111,6 +111,7 @@ class VolumesTable(volumes_tables.VolumesTable):
|
||||
row_actions = (volumes_tables.DeleteVolume,
|
||||
UpdateVolumeStatusAction,
|
||||
UnmanageVolumeAction,
|
||||
MigrateVolume)
|
||||
MigrateVolume,
|
||||
volumes_tables.UpdateMetadata)
|
||||
columns = ('tenant', 'host', 'name', 'size', 'status', 'volume_type',
|
||||
'attachments', 'bootable', 'encryption',)
|
||||
|
@ -132,6 +132,24 @@ class CreateVolumeFromSnapshot(tables.LinkAction):
|
||||
return False
|
||||
|
||||
|
||||
class UpdateMetadata(tables.LinkAction):
|
||||
name = "update_metadata"
|
||||
verbose_name = _("Update Metadata")
|
||||
|
||||
ajax = False
|
||||
attrs = {"ng-controller": "MetadataModalHelperController as modal"}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs['preempt'] = True
|
||||
super(UpdateMetadata, self).__init__(**kwargs)
|
||||
|
||||
def get_link_url(self, datum):
|
||||
obj_id = self.table.get_object_id(datum)
|
||||
self.attrs['ng-click'] = (
|
||||
"modal.openMetadataModal('volume_snapshot', '%s', true)" % obj_id)
|
||||
return "javascript:void(0);"
|
||||
|
||||
|
||||
class UpdateRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
@ -191,7 +209,8 @@ class VolumeSnapshotsTable(volume_tables.VolumesTableBase):
|
||||
launch_actions = (LaunchSnapshotNG,) + launch_actions
|
||||
|
||||
row_actions = ((CreateVolumeFromSnapshot,) + launch_actions +
|
||||
(EditVolumeSnapshot, DeleteVolumeSnapshot))
|
||||
(EditVolumeSnapshot, DeleteVolumeSnapshot,
|
||||
UpdateMetadata))
|
||||
row_class = UpdateRow
|
||||
status_columns = ("status",)
|
||||
permissions = [(
|
||||
|
@ -469,6 +469,23 @@ class VolumesFilterAction(tables.FilterAction):
|
||||
if q in volume.name.lower()]
|
||||
|
||||
|
||||
class UpdateMetadata(tables.LinkAction):
|
||||
name = "update_metadata"
|
||||
verbose_name = _("Update Metadata")
|
||||
ajax = False
|
||||
attrs = {"ng-controller": "MetadataModalHelperController as modal"}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs['preempt'] = True
|
||||
super(UpdateMetadata, self).__init__(**kwargs)
|
||||
|
||||
def get_link_url(self, datum):
|
||||
obj_id = self.table.get_object_id(datum)
|
||||
self.attrs['ng-click'] = (
|
||||
"modal.openMetadataModal('volume', '%s', true)" % obj_id)
|
||||
return "javascript:void(0);"
|
||||
|
||||
|
||||
class VolumesTable(VolumesTableBase):
|
||||
name = tables.WrappingColumn("name",
|
||||
verbose_name=_("Name"),
|
||||
@ -504,7 +521,7 @@ class VolumesTable(VolumesTableBase):
|
||||
launch_actions +
|
||||
(EditAttachments, CreateSnapshot, CreateBackup,
|
||||
RetypeVolume, UploadToImage, CreateTransfer,
|
||||
DeleteTransfer, DeleteVolume))
|
||||
DeleteTransfer, DeleteVolume, UpdateMetadata))
|
||||
|
||||
|
||||
class DetachVolume(tables.BatchAction):
|
||||
|
@ -22,7 +22,8 @@
|
||||
|
||||
metadataService.$inject = [
|
||||
'horizon.app.core.openstack-service-api.nova',
|
||||
'horizon.app.core.openstack-service-api.glance'
|
||||
'horizon.app.core.openstack-service-api.glance',
|
||||
'horizon.app.core.openstack-service-api.cinder'
|
||||
];
|
||||
|
||||
/**
|
||||
@ -32,7 +33,7 @@
|
||||
*
|
||||
* Unified acquisition and modification of metadata.
|
||||
*/
|
||||
function metadataService(nova, glance) {
|
||||
function metadataService(nova, glance, cinder) {
|
||||
var service = {
|
||||
getMetadata: getMetadata,
|
||||
editMetadata: editMetadata,
|
||||
@ -52,7 +53,10 @@
|
||||
aggregate: nova.getAggregateExtraSpecs,
|
||||
flavor: nova.getFlavorExtraSpecs,
|
||||
image: glance.getImageProps,
|
||||
instance: nova.getInstanceMetadata
|
||||
instance: nova.getInstanceMetadata,
|
||||
volume: cinder.getVolumeMetadata,
|
||||
volume_snapshot: cinder.getVolumeSnapshotMetadata,
|
||||
volume_type: cinder.getVolumeTypeMetadata
|
||||
}[resource](id);
|
||||
}
|
||||
|
||||
@ -69,7 +73,10 @@
|
||||
aggregate: nova.editAggregateExtraSpecs,
|
||||
flavor: nova.editFlavorExtraSpecs,
|
||||
image: glance.editImageProps,
|
||||
instance: nova.editInstanceMetadata
|
||||
instance: nova.editInstanceMetadata,
|
||||
volume: cinder.editVolumeMetadata,
|
||||
volume_snapshot: cinder.editVolumeSnapshotMetadata,
|
||||
volume_type: cinder.editVolumeTypeMetadata
|
||||
}[resource](id, updated, removed);
|
||||
}
|
||||
|
||||
@ -86,7 +93,10 @@
|
||||
aggregate: 'OS::Nova::Aggregate',
|
||||
flavor: 'OS::Nova::Flavor',
|
||||
image: 'OS::Glance::Image',
|
||||
instance: 'OS::Nova::Server'
|
||||
instance: 'OS::Nova::Server',
|
||||
volume: 'OS::Cinder::Volume',
|
||||
volume_snapshot: 'OS::Cinder::Snapshot',
|
||||
volume_type: 'OS:Cinder::VolumeType'
|
||||
}[resource]
|
||||
};
|
||||
if (propertiesTarget) {
|
||||
|
@ -31,10 +31,17 @@
|
||||
editImageProps: function() {},
|
||||
getNamespaces: function() {}};
|
||||
|
||||
var cinder = {getVolumeMetadata:function() {},
|
||||
getVolumeSnapshotMetadata:function() {},
|
||||
getVolumeTypeMetadata:function() {},
|
||||
editVolumeMetadata: function() {},
|
||||
editVolumeSnapshotMetadata: function() {}};
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
$provide.value('horizon.app.core.openstack-service-api.nova', nova);
|
||||
$provide.value('horizon.app.core.openstack-service-api.glance', glance);
|
||||
$provide.value('horizon.app.core.openstack-service-api.cinder', cinder);
|
||||
});
|
||||
});
|
||||
|
||||
@ -97,6 +104,18 @@
|
||||
expect(glance.editImageProps).toHaveBeenCalledWith('1', 'updated', ['removed']);
|
||||
});
|
||||
|
||||
it('should edit volume metadata', function() {
|
||||
spyOn(cinder, 'editVolumeMetadata');
|
||||
metadataService.editMetadata('volume', '1', 'updated', ['removed']);
|
||||
expect(cinder.editVolumeMetadata).toHaveBeenCalledWith('1', 'updated', ['removed']);
|
||||
});
|
||||
|
||||
it('should edit volume snapshot metadata', function() {
|
||||
spyOn(cinder, 'editVolumeSnapshotMetadata');
|
||||
metadataService.editMetadata('volume_snapshot', '1', 'updated', ['removed']);
|
||||
expect(cinder.editVolumeSnapshotMetadata).toHaveBeenCalledWith('1', 'updated', ['removed']);
|
||||
});
|
||||
|
||||
it('should get image namespace', function() {
|
||||
spyOn(glance, 'getNamespaces');
|
||||
metadataService.getNamespaces('image');
|
||||
@ -111,6 +130,13 @@
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should get volume metadata', function() {
|
||||
var expected = 'volume metadata';
|
||||
spyOn(cinder, 'getVolumeMetadata').and.returnValue(expected);
|
||||
var actual = metadataService.getMetadata('volume', '1');
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should edit instance metadata', function() {
|
||||
spyOn(nova, 'editInstanceMetadata');
|
||||
metadataService.editMetadata('instance', '1', 'updated', ['removed']);
|
||||
|
@ -7,6 +7,9 @@
|
||||
<span translate ng-if="modal.resourceType==='flavor'">Update Flavor Metadata</span>
|
||||
<span translate ng-if="modal.resourceType==='image'">Update Image Metadata</span>
|
||||
<span translate ng-if="modal.resourceType==='instance'">Update Instance Metadata</span>
|
||||
<span translate ng-if="modal.resourceType==='volume'">Update Volume Metadata</span>
|
||||
<span translate ng-if="modal.resourceType==='volume_snapshot'">Update Volume Snapshot Metadata</span>
|
||||
<span translate ng-if="modal.resourceType==='volume_type'">Update Volume Type Metadata</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
@ -38,6 +38,9 @@
|
||||
getVolumes: getVolumes,
|
||||
getVolume: getVolume,
|
||||
getVolumeTypes: getVolumeTypes,
|
||||
getVolumeMetadata: getVolumeMetadata,
|
||||
getVolumeSnapshotMetadata: getVolumeSnapshotMetadata,
|
||||
getVolumeTypeMetadata: getVolumeTypeMetadata,
|
||||
getVolumeType: getVolumeType,
|
||||
getDefaultVolumeType: getDefaultVolumeType,
|
||||
getVolumeSnapshots: getVolumeSnapshots,
|
||||
@ -48,7 +51,10 @@
|
||||
getServices: getServices,
|
||||
getDefaultQuotaSets: getDefaultQuotaSets,
|
||||
setDefaultQuotaSets: setDefaultQuotaSets,
|
||||
updateProjectQuota: updateProjectQuota
|
||||
updateProjectQuota: updateProjectQuota,
|
||||
editVolumeMetadata: editVolumeMetadata,
|
||||
editVolumeSnapshotMetadata: editVolumeSnapshotMetadata,
|
||||
editVolumeTypeMetadata:editVolumeTypeMetadata
|
||||
};
|
||||
|
||||
return service;
|
||||
@ -146,6 +152,63 @@
|
||||
});
|
||||
}
|
||||
|
||||
function getVolumeMetadata(id) {
|
||||
return apiService.get('/api/cinder/volumes/' + id + '/metadata')
|
||||
.error(function () {
|
||||
toastService.add('error', gettext('Unable to retrieve the volume metadata.'));
|
||||
});
|
||||
}
|
||||
|
||||
function getVolumeSnapshotMetadata(id) {
|
||||
return apiService.get('/api/cinder/volumesnapshots/' + id + '/metadata')
|
||||
.error(function () {
|
||||
toastService.add('error', gettext('Unable to retrieve the snapshot metadata.'));
|
||||
});
|
||||
}
|
||||
|
||||
function getVolumeTypeMetadata(id) {
|
||||
return apiService.get('/api/cinder/volumetypes/' + id + '/metadata')
|
||||
.error(function () {
|
||||
toastService.add('error', gettext('Unable to retrieve the volume type metadata.'));
|
||||
});
|
||||
}
|
||||
|
||||
function editVolumeMetadata(id, updated, removed) {
|
||||
return apiService.patch(
|
||||
'/api/cinder/volumes/' + id + '/metadata',
|
||||
{
|
||||
updated: updated,
|
||||
removed: removed
|
||||
}
|
||||
).error(function () {
|
||||
toastService.add('error', gettext('Unable to edit volume metadata.'));
|
||||
});
|
||||
}
|
||||
|
||||
function editVolumeSnapshotMetadata(id, updated, removed) {
|
||||
return apiService.patch(
|
||||
'/api/cinder/volumesnapshots/' + id + '/metadata',
|
||||
{
|
||||
updated: updated,
|
||||
removed: removed
|
||||
}
|
||||
).error(function () {
|
||||
toastService.add('error', gettext('Unable to edit snapshot metadata.'));
|
||||
});
|
||||
}
|
||||
|
||||
function editVolumeTypeMetadata(id, updated, removed) {
|
||||
return apiService.patch(
|
||||
'/api/cinder/volumetypes/' + id + '/metadata',
|
||||
{
|
||||
updated: updated,
|
||||
removed: removed
|
||||
}
|
||||
).error(function () {
|
||||
toastService.add('error', gettext('Unable to edit volume type metadata.'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name getVolumeType
|
||||
* @description
|
||||
|
@ -62,6 +62,13 @@
|
||||
error: 'Unable to retrieve the volume.',
|
||||
testInput: [1]
|
||||
},
|
||||
{
|
||||
func: 'getVolumeMetadata',
|
||||
method: 'get',
|
||||
path: '/api/cinder/volumes/1/metadata',
|
||||
error: 'Unable to retrieve the volume metadata.',
|
||||
testInput: [1]
|
||||
},
|
||||
{
|
||||
func: 'getVolumeTypes',
|
||||
method: 'get',
|
||||
@ -76,6 +83,13 @@
|
||||
error: 'Unable to retrieve the volume type.',
|
||||
testInput: [1]
|
||||
},
|
||||
{
|
||||
func: 'getVolumeTypeMetadata',
|
||||
method: 'get',
|
||||
path: '/api/cinder/volumetypes/1/metadata',
|
||||
error: 'Unable to retrieve the volume type metadata.',
|
||||
testInput: [1]
|
||||
},
|
||||
{
|
||||
func: 'getDefaultVolumeType',
|
||||
method: 'get',
|
||||
@ -112,6 +126,13 @@
|
||||
error: 'Unable to retrieve the volume snapshots.',
|
||||
testInput: [ 'config' ]
|
||||
},
|
||||
{
|
||||
func: 'getVolumeSnapshotMetadata',
|
||||
method: 'get',
|
||||
path: '/api/cinder/volumesnapshots/1/metadata',
|
||||
error: 'Unable to retrieve the snapshot metadata.',
|
||||
testInput: [1]
|
||||
},
|
||||
{
|
||||
func: 'createVolume',
|
||||
method: 'post',
|
||||
@ -165,6 +186,42 @@
|
||||
data: {'volumes': 42},
|
||||
error: 'Unable to update project quota data.',
|
||||
testInput: [{'volumes': 42}, 42]
|
||||
},
|
||||
{ func: 'editVolumeMetadata',
|
||||
method: 'patch',
|
||||
path: '/api/cinder/volumes/42/metadata',
|
||||
data: {
|
||||
"updated": {a: '1', b: '2'},
|
||||
"removed": ['c', 'd']
|
||||
},
|
||||
error: "Unable to edit volume metadata.",
|
||||
testInput: [
|
||||
42, {a: '1', b: '2'}, ['c', 'd']
|
||||
]
|
||||
},
|
||||
{ func: 'editVolumeSnapshotMetadata',
|
||||
method: 'patch',
|
||||
path: '/api/cinder/volumesnapshots/42/metadata',
|
||||
data: {
|
||||
"updated": {a: '1', b: '2'},
|
||||
"removed": ['c', 'd']
|
||||
},
|
||||
error: "Unable to edit snapshot metadata.",
|
||||
testInput: [
|
||||
42, {a: '1', b: '2'}, ['c', 'd']
|
||||
]
|
||||
},
|
||||
{ func: 'editVolumeTypeMetadata',
|
||||
method: 'patch',
|
||||
path: '/api/cinder/volumetypes/42/metadata',
|
||||
data: {
|
||||
"updated": {a: '1', b: '2'},
|
||||
"removed": ['c', 'd']
|
||||
},
|
||||
error: "Unable to edit volume type metadata.",
|
||||
testInput: [
|
||||
42, {a: '1', b: '2'}, ['c', 'd']
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -18,6 +18,7 @@ from django.conf import settings
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.api.base import Quota
|
||||
from openstack_dashboard.api.cinder import VolTypeExtraSpec
|
||||
from openstack_dashboard.api.rest import cinder
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
@ -99,6 +100,33 @@ class CinderRestTestCase(test.TestCase):
|
||||
self.assertStatusCode(response, 201)
|
||||
self.assertEqual(response.content.decode("utf-8"), mock_post_response)
|
||||
|
||||
@mock.patch.object(cinder.api, 'cinder')
|
||||
def test_volume_get_metadata(self, cc):
|
||||
request = self.mock_rest_request(**{'GET': {}})
|
||||
cc.volume_get.return_value = mock.Mock(
|
||||
**{'to_dict.return_value': {'id': 'one',
|
||||
'metadata': {'foo': 'bar'}}})
|
||||
response = cinder.VolumeMetadata().get(request, '1')
|
||||
self.assertStatusCode(response, 200)
|
||||
self.assertEqual(response.json, {'foo': 'bar'})
|
||||
cc.volume_get.assert_called_once_with(request, '1')
|
||||
|
||||
@mock.patch.object(cinder.api, 'cinder')
|
||||
def test_volume_update_metadata(self, cc):
|
||||
request = self.mock_rest_request(
|
||||
body='{"updated": {"a": "1", "b": "2"}, '
|
||||
'"removed": ["c", "d"]}'
|
||||
)
|
||||
response = cinder.VolumeMetadata().patch(request, '1')
|
||||
self.assertStatusCode(response, 204)
|
||||
self.assertEqual(b'', response.content)
|
||||
cc.volume_set_metadata.assert_called_once_with(
|
||||
request, '1', {'a': '1', 'b': '2'}
|
||||
)
|
||||
cc.volume_delete_metadata.assert_called_once_with(
|
||||
request, '1', ['c', 'd']
|
||||
)
|
||||
|
||||
#
|
||||
# Volume Types
|
||||
#
|
||||
@ -138,6 +166,35 @@ class CinderRestTestCase(test.TestCase):
|
||||
cc.volume_type_default.assert_called_once_with(request)
|
||||
cc.VolumeType.assert_called_once_with({'name': 'one'})
|
||||
|
||||
@mock.patch.object(cinder.api, 'cinder')
|
||||
def test_volume_type_get_metadata(self, cc):
|
||||
request = self.mock_rest_request(**{'GET': {}})
|
||||
cc.volume_type_extra_get = mock.Mock()
|
||||
|
||||
cc.volume_type_extra_get.return_value = \
|
||||
[VolTypeExtraSpec(1, 'foo', 'bar')]
|
||||
# cc.volume_type_extra_get.side_effect = [{'foo': 'bar'}]
|
||||
response = cinder.VolumeTypeMetadata().get(request, '1')
|
||||
self.assertStatusCode(response, 200)
|
||||
self.assertEqual(response.json, {'foo': 'bar'})
|
||||
cc.volume_type_extra_get.assert_called_once_with(request, '1')
|
||||
|
||||
@mock.patch.object(cinder.api, 'cinder')
|
||||
def test_volume_type_update_metadata(self, cc):
|
||||
request = self.mock_rest_request(
|
||||
body='{"updated": {"a": "1", "b": "2"}, '
|
||||
'"removed": ["c", "d"]}'
|
||||
)
|
||||
response = cinder.VolumeTypeMetadata().patch(request, '1')
|
||||
self.assertStatusCode(response, 204)
|
||||
self.assertEqual(b'', response.content)
|
||||
cc.volume_type_extra_set.assert_called_once_with(
|
||||
request, '1', {'a': '1', 'b': '2'}
|
||||
)
|
||||
cc.volume_type_extra_delete.assert_called_once_with(
|
||||
request, '1', ['c', 'd']
|
||||
)
|
||||
|
||||
#
|
||||
# Volume Snapshots
|
||||
#
|
||||
@ -170,6 +227,33 @@ class CinderRestTestCase(test.TestCase):
|
||||
cc.volume_snapshot_list.assert_called_once_with(request,
|
||||
search_opts=filters)
|
||||
|
||||
@mock.patch.object(cinder.api, 'cinder')
|
||||
def test_volume_snapshot_get_metadata(self, cc):
|
||||
request = self.mock_rest_request(**{'GET': {}})
|
||||
cc.volume_snapshot_get.return_value = mock.Mock(
|
||||
**{'to_dict.return_value': {'id': 'one',
|
||||
'metadata': {'foo': 'bar'}}})
|
||||
response = cinder.VolumeSnapshotMetadata().get(request, '1')
|
||||
self.assertStatusCode(response, 200)
|
||||
self.assertEqual(response.json, {'foo': 'bar'})
|
||||
cc.volume_snapshot_get.assert_called_once_with(request, '1')
|
||||
|
||||
@mock.patch.object(cinder.api, 'cinder')
|
||||
def test_volume_snapshot_update_metadata(self, cc):
|
||||
request = self.mock_rest_request(
|
||||
body='{"updated": {"a": "1", "b": "2"}, '
|
||||
'"removed": ["c", "d"]}'
|
||||
)
|
||||
response = cinder.VolumeSnapshotMetadata().patch(request, '1')
|
||||
self.assertStatusCode(response, 204)
|
||||
self.assertEqual(b'', response.content)
|
||||
cc.volume_snapshot_set_metadata.assert_called_once_with(
|
||||
request, '1', {'a': '1', 'b': '2'}
|
||||
)
|
||||
cc.volume_snapshot_delete_metadata.assert_called_once_with(
|
||||
request, '1', ['c', 'd']
|
||||
)
|
||||
|
||||
#
|
||||
# Extensions
|
||||
#
|
||||
|
@ -286,8 +286,8 @@ class GlanceApiTests(test.APITestCase):
|
||||
def test_metadefs_namespace_list_with_properties_target(self):
|
||||
metadata_defs = self.metadata_defs.list()
|
||||
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
||||
filters = {'resource_types': ['mock name'],
|
||||
'properties_target': 'mock properties target'}
|
||||
filters = {'resource_types': ['OS::Cinder::Volume'],
|
||||
'properties_target': 'user'}
|
||||
|
||||
glanceclient = self.stub_glanceclient()
|
||||
glanceclient.metadefs_namespace = self.mox.CreateMockAnything()
|
||||
@ -295,7 +295,7 @@ class GlanceApiTests(test.APITestCase):
|
||||
limit=limit,
|
||||
filters=filters,
|
||||
sort_dir='asc',
|
||||
sort_key='namespace',) \
|
||||
sort_key='namespace', ) \
|
||||
.AndReturn(metadata_defs)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
@ -404,8 +404,9 @@ def data(TEST):
|
||||
{
|
||||
'created_at': '2014-08-21T08:39:43Z',
|
||||
'prefix': 'mock',
|
||||
'name': 'mock name',
|
||||
'properties_target': 'mock properties target'
|
||||
'name': 'OS::Cinder::Volume',
|
||||
'properties_target': 'user'
|
||||
|
||||
}
|
||||
],
|
||||
'visibility': 'public',
|
||||
|
Loading…
Reference in New Issue
Block a user