Instance metadata now functionally works (completely to spec) through OSAPI

This commit is contained in:
Brian Waldon 2011-08-09 15:27:56 +00:00 committed by Tarmac
commit 9d43baf7d3
5 changed files with 114 additions and 69 deletions

View File

@ -57,18 +57,12 @@ class Controller(object):
context = req.environ['nova.context']
try:
self.compute_api.update_or_create_instance_metadata(context,
server_id,
metadata)
except exception.InstanceNotFound:
msg = _('Server does not exist')
raise exc.HTTPNotFound(explanation=msg)
new_metadata = self._update_instance_metadata(context,
server_id,
metadata,
delete=False)
except quota.QuotaError as error:
self._handle_quota_error(error)
return body
return {'metadata': new_metadata}
def update(self, req, server_id, id, body):
try:
@ -78,19 +72,22 @@ class Controller(object):
raise exc.HTTPBadRequest(explanation=expl)
try:
meta_value = meta_item.pop(id)
meta_value = meta_item[id]
except (AttributeError, KeyError):
expl = _('Request body and URI mismatch')
raise exc.HTTPBadRequest(explanation=expl)
if len(meta_item) > 0:
if len(meta_item) > 1:
expl = _('Request body contains too many items')
raise exc.HTTPBadRequest(explanation=expl)
context = req.environ['nova.context']
self._set_instance_metadata(context, server_id, meta_item)
self._update_instance_metadata(context,
server_id,
meta_item,
delete=False)
return {'meta': {id: meta_value}}
return {'meta': meta_item}
def update_all(self, req, server_id, body):
try:
@ -100,20 +97,26 @@ class Controller(object):
raise exc.HTTPBadRequest(explanation=expl)
context = req.environ['nova.context']
self._set_instance_metadata(context, server_id, metadata)
new_metadata = self._update_instance_metadata(context,
server_id,
metadata,
delete=True)
return {'metadata': metadata}
return {'metadata': new_metadata}
def _set_instance_metadata(self, context, server_id, metadata):
def _update_instance_metadata(self, context, server_id, metadata,
delete=False):
try:
self.compute_api.update_or_create_instance_metadata(context,
server_id,
metadata)
return self.compute_api.update_instance_metadata(context,
server_id,
metadata,
delete)
except exception.InstanceNotFound:
msg = _('Server does not exist')
raise exc.HTTPNotFound(explanation=msg)
except ValueError:
except (ValueError, AttributeError):
msg = _("Malformed request body")
raise exc.HTTPBadRequest(explanation=msg)
@ -138,12 +141,12 @@ class Controller(object):
metadata = self._get_metadata(context, server_id)
try:
meta_key = metadata[id]
meta_value = metadata[id]
except KeyError:
msg = _("Metadata item was not found")
raise exc.HTTPNotFound(explanation=msg)
self.compute_api.delete_instance_metadata(context, server_id, meta_key)
self.compute_api.delete_instance_metadata(context, server_id, id)
def _handle_quota_error(self, error):
"""Reraise quota errors as api-specific http exceptions."""

View File

@ -1214,11 +1214,20 @@ class API(base.Base):
"""Delete the given metadata item from an instance."""
self.db.instance_metadata_delete(context, instance_id, key)
def update_or_create_instance_metadata(self, context, instance_id,
metadata):
"""Updates or creates instance metadata."""
combined_metadata = self.get_instance_metadata(context, instance_id)
combined_metadata.update(metadata)
self._check_metadata_properties_quota(context, combined_metadata)
self.db.instance_metadata_update_or_create(context, instance_id,
metadata)
def update_instance_metadata(self, context, instance_id,
metadata, delete=False):
"""Updates or creates instance 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_instance_metadata(context, instance_id)
_metadata.update(metadata)
self._check_metadata_properties_quota(context, _metadata)
self.db.instance_metadata_update(context, instance_id, _metadata, True)
return _metadata

View File

@ -1381,9 +1381,9 @@ def instance_metadata_delete(context, instance_id, key):
IMPL.instance_metadata_delete(context, instance_id, key)
def instance_metadata_update_or_create(context, instance_id, metadata):
"""Create or update instance metadata."""
IMPL.instance_metadata_update_or_create(context, instance_id, metadata)
def instance_metadata_update(context, instance_id, metadata, delete):
"""Update metadata if it exists, otherwise create it."""
IMPL.instance_metadata_update(context, instance_id, metadata, delete)
####################

View File

@ -1346,9 +1346,10 @@ def instance_update(context, instance_id, values):
session = get_session()
metadata = values.get('metadata')
if metadata is not None:
instance_metadata_delete_all(context, instance_id)
instance_metadata_update_or_create(context, instance_id,
values.pop('metadata'))
instance_metadata_update(context,
instance_id,
values.pop('metadata'),
delete=True)
with session.begin():
if utils.is_uuid_like(instance_id):
instance_ref = instance_get_by_uuid(context, instance_id,
@ -3218,21 +3219,37 @@ def instance_metadata_get_item(context, instance_id, key, session=None):
@require_context
@require_instance_exists
def instance_metadata_update_or_create(context, instance_id, metadata):
def instance_metadata_update(context, instance_id, metadata, delete):
session = get_session()
original_metadata = instance_metadata_get(context, instance_id)
# Set existing metadata to deleted if delete argument is True
if delete:
original_metadata = instance_metadata_get(context, instance_id)
for meta_key, meta_value in original_metadata.iteritems():
if meta_key not in metadata:
meta_ref = instance_metadata_get_item(context, instance_id,
meta_key, session)
meta_ref.update({'deleted': True})
meta_ref.save(session=session)
meta_ref = None
for key, value in metadata.iteritems():
# Now update all existing items with new values, or create new meta objects
for meta_key, meta_value in metadata.iteritems():
# update the value whether it exists or not
item = {"value": meta_value}
try:
meta_ref = instance_metadata_get_item(context, instance_id, key,
session)
meta_ref = instance_metadata_get_item(context, instance_id,
meta_key, session)
# if the item doesn't exist, we also need to set key and instance_id
except exception.InstanceMetadataNotFound, e:
meta_ref = models.InstanceMetadata()
meta_ref.update({"key": key, "value": value,
"instance_id": instance_id,
"deleted": False})
item.update({"key": meta_key, "instance_id": instance_id})
meta_ref.update(item)
meta_ref.save(session=session)
return metadata

View File

@ -29,11 +29,11 @@ import nova.wsgi
FLAGS = flags.FLAGS
def return_create_instance_metadata_max(context, server_id, metadata):
def return_create_instance_metadata_max(context, server_id, metadata, delete):
return stub_max_server_metadata()
def return_create_instance_metadata(context, server_id, metadata):
def return_create_instance_metadata(context, server_id, metadata, delete):
return stub_server_metadata()
@ -202,21 +202,30 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(404, res.status_int)
def test_create(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_get',
return_server_metadata)
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
req = webob.Request.blank('/v1.1/servers/1/metadata')
req.method = 'POST'
req.content_type = "application/json"
expected = {"metadata": {"key1": "value1"}}
req.body = json.dumps(expected)
input = {"metadata": {"key9": "value9"}}
req.body = json.dumps(input)
res = req.get_response(fakes.wsgi_app())
self.assertEqual(200, res.status_int)
res_dict = json.loads(res.body)
self.assertEqual(expected, res_dict)
input['metadata'].update({
"key1": "value1",
"key2": "value2",
"key3":"value3",
})
self.assertEqual(input, res_dict)
def test_create_xml(self):
self.stubs.Set(nova.db.api, "instance_metadata_update_or_create",
self.stubs.Set(nova.db.api, 'instance_metadata_get',
return_server_metadata)
self.stubs.Set(nova.db.api, "instance_metadata_update",
return_create_instance_metadata)
req = webob.Request.blank("/v1.1/servers/1/metadata")
req.method = "POST"
@ -225,22 +234,29 @@ class ServerMetaDataTest(test.TestCase):
request_metadata = minidom.parseString("""
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
<meta key="key3">value3</meta>
<meta key="key2">value2</meta>
<meta key="key1">value1</meta>
<meta key="key5">value5</meta>
</metadata>
""".replace(" ", "").replace("\n", ""))
req.body = str(request_metadata.toxml())
response = req.get_response(fakes.wsgi_app())
expected_metadata = minidom.parseString("""
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
<meta key="key3">value3</meta>
<meta key="key2">value2</meta>
<meta key="key1">value1</meta>
<meta key="key5">value5</meta>
</metadata>
""".replace(" ", "").replace("\n", ""))
self.assertEqual(200, response.status_int)
actual_metadata = minidom.parseString(response.body)
self.assertEqual(request_metadata.toxml(), actual_metadata.toxml())
self.assertEqual(expected_metadata.toxml(), actual_metadata.toxml())
def test_create_empty_body(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
req = webob.Request.blank('/v1.1/servers/1/metadata')
req.method = 'POST'
@ -258,7 +274,7 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(404, res.status_int)
def test_update_all(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
req = webob.Request.blank('/v1.1/servers/1/metadata')
req.method = 'PUT'
@ -276,7 +292,7 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(expected, res_dict)
def test_update_all_empty_container(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
req = webob.Request.blank('/v1.1/servers/1/metadata')
req.method = 'PUT'
@ -289,7 +305,7 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(expected, res_dict)
def test_update_all_malformed_container(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
req = webob.Request.blank('/v1.1/servers/1/metadata')
req.method = 'PUT'
@ -300,7 +316,7 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(400, res.status_int)
def test_update_all_malformed_data(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
req = webob.Request.blank('/v1.1/servers/1/metadata')
req.method = 'PUT'
@ -320,7 +336,7 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(404, res.status_int)
def test_update_item(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
req = webob.Request.blank('/v1.1/servers/1/metadata/key1')
req.method = 'PUT'
@ -334,7 +350,7 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(expected, res_dict)
def test_update_item_xml(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
req = webob.Request.blank('/v1.1/servers/1/metadata/key9')
req.method = 'PUT'
@ -361,7 +377,7 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(404, res.status_int)
def test_update_item_empty_body(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
req = webob.Request.blank('/v1.1/servers/1/metadata/key1')
req.method = 'PUT'
@ -370,7 +386,7 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(400, res.status_int)
def test_update_item_too_many_keys(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
req = webob.Request.blank('/v1.1/servers/1/metadata/key1')
req.method = 'PUT'
@ -380,7 +396,7 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(400, res.status_int)
def test_update_item_body_uri_mismatch(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
req = webob.Request.blank('/v1.1/servers/1/metadata/bad')
req.method = 'PUT'
@ -390,7 +406,7 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(400, res.status_int)
def test_too_many_metadata_items_on_create(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata)
data = {"metadata": {}}
for num in range(FLAGS.quota_metadata_items + 1):
@ -404,7 +420,7 @@ class ServerMetaDataTest(test.TestCase):
self.assertEqual(400, res.status_int)
def test_to_many_metadata_items_on_update_item(self):
self.stubs.Set(nova.db.api, 'instance_metadata_update_or_create',
self.stubs.Set(nova.db.api, 'instance_metadata_update',
return_create_instance_metadata_max)
req = webob.Request.blank('/v1.1/servers/1/metadata/key1')
req.method = 'PUT'