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 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)
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user