added volume metadata APIs (OS & volume layers), search volume by metadata & other
This commit is contained in:
@@ -24,6 +24,7 @@ from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import quota
|
||||
from nova import volume
|
||||
from nova.volume import volume_types
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import faults
|
||||
@@ -63,6 +64,22 @@ def _translate_volume_summary_view(context, vol):
|
||||
|
||||
d['displayName'] = vol['display_name']
|
||||
d['displayDescription'] = vol['display_description']
|
||||
|
||||
if vol['volume_type_id'] and vol.get('volume_type'):
|
||||
d['volumeType'] = vol['volume_type']['name']
|
||||
else:
|
||||
d['volumeType'] = vol['volume_type_id']
|
||||
|
||||
LOG.audit(_("vol=%s"), vol, context=context)
|
||||
|
||||
if vol.get('volume_metadata'):
|
||||
meta_dict = {}
|
||||
for i in vol['volume_metadata']:
|
||||
meta_dict[i['key']] = i['value']
|
||||
d['metadata'] = meta_dict
|
||||
else:
|
||||
d['metadata'] = {}
|
||||
|
||||
return d
|
||||
|
||||
|
||||
@@ -80,6 +97,8 @@ class VolumeController(object):
|
||||
"createdAt",
|
||||
"displayName",
|
||||
"displayDescription",
|
||||
"volumeType",
|
||||
"metadata",
|
||||
]}}}
|
||||
|
||||
def __init__(self):
|
||||
@@ -136,12 +155,25 @@ class VolumeController(object):
|
||||
vol = body['volume']
|
||||
size = vol['size']
|
||||
LOG.audit(_("Create volume of %s GB"), size, context=context)
|
||||
|
||||
vol_type = vol.get('volume_type', None)
|
||||
if vol_type:
|
||||
try:
|
||||
vol_type = volume_types.get_volume_type_by_name(context,
|
||||
vol_type)
|
||||
except exception.NotFound:
|
||||
return faults.Fault(exc.HTTPNotFound())
|
||||
|
||||
metadata = vol.get('metadata', None)
|
||||
|
||||
new_volume = self.volume_api.create(context, size, None,
|
||||
vol.get('display_name'),
|
||||
vol.get('display_description'))
|
||||
vol.get('display_description'),
|
||||
volume_type=vol_type,
|
||||
metadata=metadata)
|
||||
|
||||
# Work around problem that instance is lazy-loaded...
|
||||
new_volume['instance'] = None
|
||||
new_volume = self.volume_api.get(context, new_volume['id'])
|
||||
|
||||
retval = _translate_volume_detail_view(context, new_volume)
|
||||
|
||||
|
||||
@@ -1020,11 +1020,11 @@ def virtual_interface_delete_by_instance(context, instance_id):
|
||||
###################
|
||||
|
||||
|
||||
def _metadata_refs(metadata_dict):
|
||||
def _metadata_refs(metadata_dict, meta_class):
|
||||
metadata_refs = []
|
||||
if metadata_dict:
|
||||
for k, v in metadata_dict.iteritems():
|
||||
metadata_ref = models.InstanceMetadata()
|
||||
metadata_ref = meta_class()
|
||||
metadata_ref['key'] = k
|
||||
metadata_ref['value'] = v
|
||||
metadata_refs.append(metadata_ref)
|
||||
@@ -1038,8 +1038,8 @@ def instance_create(context, values):
|
||||
context - request context object
|
||||
values - dict containing column values.
|
||||
"""
|
||||
values['metadata'] = _metadata_refs(values.get('metadata'))
|
||||
|
||||
values['metadata'] = _metadata_refs(values.get('metadata'),
|
||||
models.InstanceMetadata)
|
||||
instance_ref = models.Instance()
|
||||
instance_ref['uuid'] = str(utils.gen_uuid())
|
||||
|
||||
@@ -2097,8 +2097,8 @@ def volume_attached(context, volume_id, instance_id, mountpoint):
|
||||
|
||||
@require_context
|
||||
def volume_create(context, values):
|
||||
values['metadata'] = _metadata_refs(values.get('metadata'))
|
||||
|
||||
values['volume_metadata'] = _metadata_refs(values.get('metadata'),
|
||||
models.VolumeMetadata)
|
||||
volume_ref = models.Volume()
|
||||
volume_ref.update(values)
|
||||
|
||||
@@ -3617,14 +3617,10 @@ def volume_type_create(_context, values):
|
||||
"""
|
||||
try:
|
||||
specs = values.get('extra_specs')
|
||||
specs_refs = []
|
||||
if specs:
|
||||
for k, v in specs.iteritems():
|
||||
specs_ref = models.VolumeTypeExtraSpecs()
|
||||
specs_ref['key'] = k
|
||||
specs_ref['value'] = v
|
||||
specs_refs.append(specs_ref)
|
||||
values['extra_specs'] = specs_refs
|
||||
|
||||
values['extra_specs'] = _metadata_refs(values.get('extra_specs'),
|
||||
models.VolumeTypeExtraSpecs)
|
||||
|
||||
volume_type_ref = models.VolumeTypes()
|
||||
volume_type_ref.update(values)
|
||||
volume_type_ref.save()
|
||||
|
||||
@@ -285,6 +285,22 @@ class VolumesTest(integrated_helpers._IntegratedTestBase):
|
||||
self.assertEquals(undisco_move['mountpoint'], device)
|
||||
self.assertEquals(undisco_move['instance_id'], server_id)
|
||||
|
||||
def test_create_volume_with_metadata(self):
|
||||
"""Creates and deletes a volume."""
|
||||
|
||||
# Create volume
|
||||
metadata = {'key1': 'value1',
|
||||
'key2': 'value2'}
|
||||
created_volume = self.api.post_volume({'volume': {'size': 1,
|
||||
'metadata': metadata}})
|
||||
LOG.debug("created_volume: %s" % created_volume)
|
||||
self.assertTrue(created_volume['id'])
|
||||
created_volume_id = created_volume['id']
|
||||
|
||||
# Check it's there and metadata present
|
||||
found_volume = self.api.get_volume(created_volume_id)
|
||||
self.assertEqual(created_volume_id, found_volume['id'])
|
||||
self.assertEqual(metadata, found_volume['metadata'])
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -61,6 +61,11 @@ class API(base.Base):
|
||||
if availability_zone is None:
|
||||
availability_zone = FLAGS.storage_availability_zone
|
||||
|
||||
if volume_type is None:
|
||||
volume_type_id = None
|
||||
else:
|
||||
volume_type_id = volume_type.get('id', None)
|
||||
|
||||
options = {
|
||||
'size': size,
|
||||
'user_id': context.user_id,
|
||||
@@ -71,7 +76,7 @@ class API(base.Base):
|
||||
'attach_status': "detached",
|
||||
'display_name': name,
|
||||
'display_description': description,
|
||||
'volume_type_id': volume_type.get('id', None),
|
||||
'volume_type_id': volume_type_id,
|
||||
'metadata': metadata,
|
||||
}
|
||||
|
||||
@@ -112,10 +117,44 @@ class API(base.Base):
|
||||
rv = self.db.volume_get(context, volume_id)
|
||||
return dict(rv.iteritems())
|
||||
|
||||
def get_all(self, context):
|
||||
def get_all(self, context, search_opts={}):
|
||||
if context.is_admin:
|
||||
return self.db.volume_get_all(context)
|
||||
return self.db.volume_get_all_by_project(context, context.project_id)
|
||||
volumes = self.db.volume_get_all(context)
|
||||
else:
|
||||
volumes = self.db.volume_get_all_by_project(context,
|
||||
context.project_id)
|
||||
|
||||
if search_opts:
|
||||
LOG.debug(_("Searching by: %s") % str(search_opts))
|
||||
|
||||
def _check_metadata_match(volume, searchdict):
|
||||
volume_metadata = {}
|
||||
for i in volume.get('volume_metadata'):
|
||||
volume_metadata[i['key']] = i['value']
|
||||
|
||||
for k, v in searchdict:
|
||||
if k not in volume_metadata.keys()\
|
||||
or volume_metadata[k] != v:
|
||||
return False
|
||||
return True
|
||||
|
||||
# search_option to filter_name mapping.
|
||||
filter_mapping = {'metadata': _check_metadata_match}
|
||||
|
||||
for volume in volumes:
|
||||
# go over all filters in the list
|
||||
for opt, values in search_opts.iteritems():
|
||||
try:
|
||||
filter_func = filter_mapping[opt]
|
||||
except KeyError:
|
||||
# no such filter - ignore it, go to next filter
|
||||
continue
|
||||
else:
|
||||
if filter_func(volume, values) == False:
|
||||
# if one of conditions didn't match - remove
|
||||
volumes.remove(volume)
|
||||
break
|
||||
return volumes
|
||||
|
||||
def get_snapshot(self, context, snapshot_id):
|
||||
rv = self.db.snapshot_get(context, snapshot_id)
|
||||
@@ -190,3 +229,29 @@ class API(base.Base):
|
||||
{"method": "delete_snapshot",
|
||||
"args": {"topic": FLAGS.volume_topic,
|
||||
"snapshot_id": snapshot_id}})
|
||||
|
||||
def get_volume_metadata(self, context, volume_id):
|
||||
"""Get all metadata associated with a volume."""
|
||||
rv = self.db.volume_metadata_get(context, volume_id)
|
||||
return dict(rv.iteritems())
|
||||
|
||||
def delete_volume_metadata(self, context, volume_id, key):
|
||||
"""Delete the given metadata item from an volume."""
|
||||
self.db.volume_metadata_delete(context, volume_id, key)
|
||||
|
||||
def update_volume_metadata(self, context, volume_id,
|
||||
metadata, delete=False):
|
||||
"""Updates or creates volume metadata.
|
||||
|
||||
If delete is True, metadata items that are not specified in the
|
||||
`metadata` argument will be deleted.
|
||||
|
||||
"""
|
||||
if delete:
|
||||
_metadata = metadata
|
||||
else:
|
||||
_metadata = self.get_volume_metadata(context, volume_id)
|
||||
_metadata.update(metadata)
|
||||
|
||||
self.db.volume_metadata_update(context, volume_id, _metadata, True)
|
||||
return _metadata
|
||||
|
||||
Reference in New Issue
Block a user