summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvladimir.p <vladimir@zadarastorage.com>2011-08-23 20:22:27 -0700
committervladimir.p <vladimir@zadarastorage.com>2011-08-23 20:22:27 -0700
commit29940dd27f3a40a4ad54bc2f7a4cea5ac2226b83 (patch)
tree9f67cccca209f1eada4e2fa699f2a50852bde20c
parentddc7d9470674a4d7300d15e5c6fa54b784b6a36f (diff)
downloadnova-29940dd27f3a40a4ad54bc2f7a4cea5ac2226b83.tar.gz
nova-29940dd27f3a40a4ad54bc2f7a4cea5ac2226b83.tar.xz
nova-29940dd27f3a40a4ad54bc2f7a4cea5ac2226b83.zip
added volume metadata APIs (OS & volume layers), search volume by metadata & other
-rw-r--r--nova/api/openstack/contrib/volumes.py36
-rw-r--r--nova/db/sqlalchemy/api.py24
-rw-r--r--nova/tests/integrated/test_volumes.py16
-rw-r--r--nova/volume/api.py73
4 files changed, 129 insertions, 20 deletions
diff --git a/nova/api/openstack/contrib/volumes.py b/nova/api/openstack/contrib/volumes.py
index 867fe301e..d62225e58 100644
--- a/nova/api/openstack/contrib/volumes.py
+++ b/nova/api/openstack/contrib/volumes.py
@@ -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)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index ac8aed307..f14f95ab0 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -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()
diff --git a/nova/tests/integrated/test_volumes.py b/nova/tests/integrated/test_volumes.py
index d3e936462..86165944f 100644
--- a/nova/tests/integrated/test_volumes.py
+++ b/nova/tests/integrated/test_volumes.py
@@ -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()
diff --git a/nova/volume/api.py b/nova/volume/api.py
index 80e8bd85f..195ab24aa 100644
--- a/nova/volume/api.py
+++ b/nova/volume/api.py
@@ -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