added volume metadata APIs (OS & volume layers), search volume by metadata & other

This commit is contained in:
vladimir.p
2011-08-23 20:22:27 -07:00
parent ddc7d94706
commit 29940dd27f
4 changed files with 129 additions and 20 deletions

View File

@@ -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)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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