diff options
-rw-r--r-- | nova/api/openstack/contrib/volumes.py | 36 | ||||
-rw-r--r-- | nova/db/sqlalchemy/api.py | 24 | ||||
-rw-r--r-- | nova/tests/integrated/test_volumes.py | 16 | ||||
-rw-r--r-- | nova/volume/api.py | 73 |
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 |