Add methods to get image metadata from instance
This patch adds a couple of utility functions that enclose all the logic for getting and parsing the image metadata stored in the instance's system metadata. First, this will try to fetch the metadata from the real image and will prevent it from failing if it is not available. It will be then merged with the image metadata stored during the instance creation. Related to bug #1039662 Change-Id: I2130caf19858585571b1199e27f0a98ad5f08701
This commit is contained in:
parent
42efb509d0
commit
8e575be75c
|
@ -225,6 +225,29 @@ def _get_unused_letter(used_letters):
|
|||
return letters[0]
|
||||
|
||||
|
||||
def get_image_metadata(context, image_service, image_id, instance):
|
||||
# If the base image is still available, get its metadata
|
||||
try:
|
||||
image = image_service.show(context, image_id)
|
||||
except Exception as e:
|
||||
LOG.warning(_("Can't access image %(image_id)s: %(error)s"),
|
||||
{"image_id": image_id, "error": e}, instance=instance)
|
||||
image_system_meta = {}
|
||||
else:
|
||||
instance_type = flavors.extract_flavor(instance)
|
||||
image_system_meta = utils.get_system_metadata_from_image(
|
||||
image, instance_type)
|
||||
|
||||
# Get the system metadata from the instance
|
||||
system_meta = utils.instance_sys_meta(instance)
|
||||
|
||||
# Merge the metadata from the instance with the image's, if any
|
||||
system_meta.update(image_system_meta)
|
||||
|
||||
# Convert the system metadata to image metadata
|
||||
return utils.get_image_from_system_metadata(system_meta)
|
||||
|
||||
|
||||
def notify_usage_exists(notifier, context, instance_ref, current_period=False,
|
||||
ignore_missing_network_data=True,
|
||||
system_metadata=None, extra_usage_info=None):
|
||||
|
|
|
@ -31,9 +31,11 @@ from nova import exception
|
|||
from nova.image import glance
|
||||
from nova.network import api as network_api
|
||||
from nova import notifier as notify
|
||||
from nova.objects import instance as instance_obj
|
||||
from nova.openstack.common import importutils
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova import test
|
||||
from nova.tests import fake_instance
|
||||
from nova.tests import fake_instance_actions
|
||||
from nova.tests import fake_network
|
||||
from nova.tests import fake_notifier
|
||||
|
@ -589,3 +591,98 @@ class UsageInfoTestCase(test.TestCase):
|
|||
"create.start",
|
||||
aggregate_payload)
|
||||
self.assertEquals(len(fake_notifier.NOTIFICATIONS), 0)
|
||||
|
||||
|
||||
class ComputeGetImageMetadataTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(ComputeGetImageMetadataTestCase, self).setUp()
|
||||
self.context = context.RequestContext('fake', 'fake')
|
||||
|
||||
self.image = {
|
||||
"min_ram": 10,
|
||||
"min_disk": 1,
|
||||
"disk_format": "raw",
|
||||
"container_format": "bare",
|
||||
"properties": {},
|
||||
}
|
||||
|
||||
self.image_service = nova.tests.image.fake._FakeImageService()
|
||||
self.stubs.Set(self.image_service, 'show', self._fake_show)
|
||||
|
||||
self.ctx = context.RequestContext('fake', 'fake')
|
||||
|
||||
sys_meta = {
|
||||
'image_min_ram': 10,
|
||||
'image_min_disk': 1,
|
||||
'image_disk_format': 'raw',
|
||||
'image_container_format': 'bare',
|
||||
'instance_type_id': 0,
|
||||
'instance_type_name': 'm1.fake',
|
||||
'instance_type_memory_mb': 10,
|
||||
'instance_type_vcpus': 1,
|
||||
'instance_type_root_gb': 1,
|
||||
'instance_type_ephemeral_gb': 1,
|
||||
'instance_type_flavorid': '0',
|
||||
'instance_type_swap': 1,
|
||||
'instance_type_rxtx_factor': 0.0,
|
||||
'instance_type_vcpu_weight': None,
|
||||
}
|
||||
|
||||
self.instance = fake_instance.fake_db_instance(
|
||||
memory_mb=0, root_gb=0,
|
||||
system_metadata=sys_meta)
|
||||
|
||||
@property
|
||||
def instance_obj(self):
|
||||
return instance_obj.Instance._from_db_object(
|
||||
self.ctx, instance_obj.Instance(), self.instance,
|
||||
expected_attrs=instance_obj.INSTANCE_DEFAULT_FIELDS)
|
||||
|
||||
def _fake_show(self, ctx, image_id):
|
||||
return self.image
|
||||
|
||||
def test_get_image_meta(self):
|
||||
image_meta = compute_utils.get_image_metadata(
|
||||
self.ctx, self.image_service, 'fake-image', self.instance_obj)
|
||||
|
||||
self.image['properties'] = 'DONTCARE'
|
||||
self.assertThat(self.image, matchers.DictMatches(image_meta))
|
||||
|
||||
def test_get_image_meta_no_image(self):
|
||||
def fake_show(ctx, image_id):
|
||||
raise exception.ImageNotFound(image_id='fake-image')
|
||||
|
||||
self.stubs.Set(self.image_service, 'show', fake_show)
|
||||
|
||||
image_meta = compute_utils.get_image_metadata(
|
||||
self.ctx, self.image_service, 'fake-image', self.instance_obj)
|
||||
|
||||
self.image['properties'] = 'DONTCARE'
|
||||
self.assertThat(self.image, matchers.DictMatches(image_meta))
|
||||
|
||||
def test_get_image_meta_no_image_system_meta(self):
|
||||
for k in self.instance['system_metadata'].keys():
|
||||
if k.startswith('image_'):
|
||||
del self.instance['system_metadata'][k]
|
||||
|
||||
image_meta = compute_utils.get_image_metadata(
|
||||
self.ctx, self.image_service, 'fake-image', self.instance_obj)
|
||||
|
||||
self.image['properties'] = 'DONTCARE'
|
||||
self.assertThat(self.image, matchers.DictMatches(image_meta))
|
||||
|
||||
def test_get_image_meta_no_image_no_image_system_meta(self):
|
||||
def fake_show(ctx, image_id):
|
||||
raise exception.ImageNotFound(image_id='fake-image')
|
||||
|
||||
self.stubs.Set(self.image_service, 'show', fake_show)
|
||||
|
||||
for k in self.instance['system_metadata'].keys():
|
||||
if k.startswith('image_'):
|
||||
del self.instance['system_metadata'][k]
|
||||
|
||||
image_meta = compute_utils.get_image_metadata(
|
||||
self.ctx, self.image_service, 'fake-image', self.instance_obj)
|
||||
|
||||
expected = {'properties': 'DONTCARE'}
|
||||
self.assertThat(expected, matchers.DictMatches(image_meta))
|
||||
|
|
|
@ -1028,3 +1028,58 @@ class GetSystemMetadataFromImageTestCase(test.TestCase):
|
|||
for key in utils.SM_INHERITABLE_KEYS:
|
||||
sys_key = "%s%s" % (utils.SM_IMAGE_PROP_PREFIX, key)
|
||||
self.assertTrue(sys_key not in sys_meta)
|
||||
|
||||
|
||||
class GetImageFromSystemMetadataTestCase(test.TestCase):
|
||||
def get_system_metadata(self):
|
||||
sys_meta = {
|
||||
"image_min_ram": 1,
|
||||
"image_min_disk": 1,
|
||||
"image_disk_format": "raw",
|
||||
"image_container_format": "bare",
|
||||
}
|
||||
|
||||
return sys_meta
|
||||
|
||||
def test_image_from_system_metadata(self):
|
||||
sys_meta = self.get_system_metadata()
|
||||
sys_meta["%soo1" % utils.SM_IMAGE_PROP_PREFIX] = "bar"
|
||||
sys_meta["%soo2" % utils.SM_IMAGE_PROP_PREFIX] = "baz"
|
||||
|
||||
image = utils.get_image_from_system_metadata(sys_meta)
|
||||
|
||||
# Verify that we inherit all the needed keys
|
||||
for key in utils.SM_INHERITABLE_KEYS:
|
||||
sys_key = "%s%s" % (utils.SM_IMAGE_PROP_PREFIX, key)
|
||||
self.assertEqual(image[key], sys_meta.get(sys_key))
|
||||
|
||||
# Verify that we inherit the rest of metadata as properties
|
||||
self.assertTrue("properties" in image)
|
||||
|
||||
for key, value in image["properties"].iteritems():
|
||||
sys_key = "%s%s" % (utils.SM_IMAGE_PROP_PREFIX, key)
|
||||
self.assertEqual(image["properties"][key], sys_meta[sys_key])
|
||||
|
||||
def test_dont_inherit_empty_values(self):
|
||||
sys_meta = self.get_system_metadata()
|
||||
|
||||
for key in utils.SM_INHERITABLE_KEYS:
|
||||
sys_key = "%s%s" % (utils.SM_IMAGE_PROP_PREFIX, key)
|
||||
sys_meta[sys_key] = None
|
||||
|
||||
image = utils.get_image_from_system_metadata(sys_meta)
|
||||
|
||||
# Verify that the empty properties have not been inherited
|
||||
for key in utils.SM_INHERITABLE_KEYS:
|
||||
self.assertTrue(key not in image)
|
||||
|
||||
def test_non_inheritable_image_properties(self):
|
||||
sys_meta = self.get_system_metadata()
|
||||
sys_meta["%soo1" % utils.SM_IMAGE_PROP_PREFIX] = "bar"
|
||||
|
||||
CONF.non_inheritable_image_properties = ["foo1"]
|
||||
|
||||
image = utils.get_image_from_system_metadata(sys_meta)
|
||||
|
||||
# Verify that the foo1 key has not been inherited
|
||||
self.assertTrue("foo1" not in image)
|
||||
|
|
|
@ -1255,3 +1255,33 @@ def get_system_metadata_from_image(image_meta, instance_type=None):
|
|||
system_meta[prefix_format % key] = value
|
||||
|
||||
return system_meta
|
||||
|
||||
|
||||
def get_image_from_system_metadata(system_meta):
|
||||
image_meta = {}
|
||||
properties = {}
|
||||
|
||||
if not isinstance(system_meta, dict):
|
||||
system_meta = metadata_to_dict(system_meta)
|
||||
|
||||
for key, value in system_meta.iteritems():
|
||||
if value is None:
|
||||
continue
|
||||
|
||||
# NOTE(xqueralt): Not sure this has to inherit all the properties or
|
||||
# just the ones we need. Leaving it for now to keep the old behaviour.
|
||||
if key.startswith(SM_IMAGE_PROP_PREFIX):
|
||||
key = key[len(SM_IMAGE_PROP_PREFIX):]
|
||||
|
||||
if key in SM_INHERITABLE_KEYS:
|
||||
image_meta[key] = value
|
||||
else:
|
||||
# Skip properties that are non-inheritable
|
||||
if key in CONF.non_inheritable_image_properties:
|
||||
continue
|
||||
properties[key] = value
|
||||
|
||||
if properties:
|
||||
image_meta['properties'] = properties
|
||||
|
||||
return image_meta
|
||||
|
|
Loading…
Reference in New Issue