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 log as logging
from nova import quota from nova import quota
from nova import volume from nova import volume
from nova.volume import volume_types
from nova.api.openstack import common from nova.api.openstack import common
from nova.api.openstack import extensions from nova.api.openstack import extensions
from nova.api.openstack import faults from nova.api.openstack import faults
@@ -63,6 +64,22 @@ def _translate_volume_summary_view(context, vol):
d['displayName'] = vol['display_name'] d['displayName'] = vol['display_name']
d['displayDescription'] = vol['display_description'] 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 return d
@@ -80,6 +97,8 @@ class VolumeController(object):
"createdAt", "createdAt",
"displayName", "displayName",
"displayDescription", "displayDescription",
"volumeType",
"metadata",
]}}} ]}}}
def __init__(self): def __init__(self):
@@ -136,12 +155,25 @@ class VolumeController(object):
vol = body['volume'] vol = body['volume']
size = vol['size'] size = vol['size']
LOG.audit(_("Create volume of %s GB"), size, context=context) 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, new_volume = self.volume_api.create(context, size, None,
vol.get('display_name'), 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... # 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) 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 = [] metadata_refs = []
if metadata_dict: if metadata_dict:
for k, v in metadata_dict.iteritems(): for k, v in metadata_dict.iteritems():
metadata_ref = models.InstanceMetadata() metadata_ref = meta_class()
metadata_ref['key'] = k metadata_ref['key'] = k
metadata_ref['value'] = v metadata_ref['value'] = v
metadata_refs.append(metadata_ref) metadata_refs.append(metadata_ref)
@@ -1038,8 +1038,8 @@ def instance_create(context, values):
context - request context object context - request context object
values - dict containing column values. 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 = models.Instance()
instance_ref['uuid'] = str(utils.gen_uuid()) instance_ref['uuid'] = str(utils.gen_uuid())
@@ -2097,8 +2097,8 @@ def volume_attached(context, volume_id, instance_id, mountpoint):
@require_context @require_context
def volume_create(context, values): 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 = models.Volume()
volume_ref.update(values) volume_ref.update(values)
@@ -3617,14 +3617,10 @@ def volume_type_create(_context, values):
""" """
try: try:
specs = values.get('extra_specs') specs = values.get('extra_specs')
specs_refs = []
if specs: values['extra_specs'] = _metadata_refs(values.get('extra_specs'),
for k, v in specs.iteritems(): models.VolumeTypeExtraSpecs)
specs_ref = models.VolumeTypeExtraSpecs()
specs_ref['key'] = k
specs_ref['value'] = v
specs_refs.append(specs_ref)
values['extra_specs'] = specs_refs
volume_type_ref = models.VolumeTypes() volume_type_ref = models.VolumeTypes()
volume_type_ref.update(values) volume_type_ref.update(values)
volume_type_ref.save() 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['mountpoint'], device)
self.assertEquals(undisco_move['instance_id'], server_id) 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__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@@ -61,6 +61,11 @@ class API(base.Base):
if availability_zone is None: if availability_zone is None:
availability_zone = FLAGS.storage_availability_zone 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 = { options = {
'size': size, 'size': size,
'user_id': context.user_id, 'user_id': context.user_id,
@@ -71,7 +76,7 @@ class API(base.Base):
'attach_status': "detached", 'attach_status': "detached",
'display_name': name, 'display_name': name,
'display_description': description, 'display_description': description,
'volume_type_id': volume_type.get('id', None), 'volume_type_id': volume_type_id,
'metadata': metadata, 'metadata': metadata,
} }
@@ -112,10 +117,44 @@ class API(base.Base):
rv = self.db.volume_get(context, volume_id) rv = self.db.volume_get(context, volume_id)
return dict(rv.iteritems()) return dict(rv.iteritems())
def get_all(self, context): def get_all(self, context, search_opts={}):
if context.is_admin: if context.is_admin:
return self.db.volume_get_all(context) volumes = self.db.volume_get_all(context)
return self.db.volume_get_all_by_project(context, context.project_id) 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): def get_snapshot(self, context, snapshot_id):
rv = self.db.snapshot_get(context, snapshot_id) rv = self.db.snapshot_get(context, snapshot_id)
@@ -190,3 +229,29 @@ class API(base.Base):
{"method": "delete_snapshot", {"method": "delete_snapshot",
"args": {"topic": FLAGS.volume_topic, "args": {"topic": FLAGS.volume_topic,
"snapshot_id": snapshot_id}}) "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