From 8e8a37e26e07b92069f48e210b703de6ecd3fa15 Mon Sep 17 00:00:00 2001 From: Eoghan Glynn Date: Fri, 14 Jun 2013 15:52:13 +0000 Subject: [PATCH] Capture instance metadata in reserved namespace. The concept of a reserved namespace for instance metadata is intended to support the Heat autoscaling use-case. When spinning up new instances in a autoscaling group, Heat will set some additional user metadata on the instances to identify their group membership. Previously, instance user metadata was not captured in the metering store and thus could not be queried on. Now the reserved namespace(s) are used to signal to the compute agent that some user metadata should not be discarded. This allows the statistics for a group of associated instances to be aggregated in a single query. Change-Id: I838db38bf2e01a0e5e4d76f3f401d7f41e1dff57 --- ceilometer/compute/instance.py | 34 +++++++++++++++++++++++++++++++++- tests/compute/test_instance.py | 9 ++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/ceilometer/compute/instance.py b/ceilometer/compute/instance.py index dff5ec6e0..7b312ef6d 100644 --- a/ceilometer/compute/instance.py +++ b/ceilometer/compute/instance.py @@ -17,6 +17,19 @@ # under the License. """Common code for working with instances """ +from oslo.config import cfg + +OPTS = [ + cfg.ListOpt('reserved_metadata_namespace', + default=['metering.'], + help='list of metadata prefixes resevred for metering use', + ), + cfg.IntOpt('reserved_metadata_length', + default=256, + help='limit on length of reserved metadata values'), +] + +cfg.CONF.register_opts(OPTS) INSTANCE_PROPERTIES = [ # Identity properties @@ -36,6 +49,24 @@ INSTANCE_PROPERTIES = [ 'vcpus'] +def add_reserved_user_metadata(instance, metadata): + limit = cfg.CONF.reserved_metadata_length + user_metadata = {} + for prefix in cfg.CONF.reserved_metadata_namespace: + md = dict( + (k[len(prefix):].replace('.', '_'), + v[:limit] if isinstance(v, basestring) else v) + for k, v in instance.metadata.items() + if (k.startswith(prefix) and + k[len(prefix):].replace('.', '_') not in metadata) + ) + user_metadata.update(md) + if user_metadata: + metadata['user_metadata'] = user_metadata + + return metadata + + def get_metadata_from_object(instance): """Return a metadata dictionary for the instance. """ @@ -57,4 +88,5 @@ def get_metadata_from_object(instance): for name in INSTANCE_PROPERTIES: metadata[name] = getattr(instance, name, u'') - return metadata + + return add_reserved_user_metadata(instance, metadata) diff --git a/tests/compute/test_instance.py b/tests/compute/test_instance.py index e5bb06148..092213840 100644 --- a/tests/compute/test_instance.py +++ b/tests/compute/test_instance.py @@ -67,7 +67,10 @@ class TestLocationMetadata(base.TestCase): 'links': [{"rel": "bookmark", 'href': 2}]}, 'flavor': {'id': 1}, - 'hostId': '1234-5678'} + 'hostId': '1234-5678', + 'metadata': {'metering.autoscale.group': + 'X' * 512, + 'metering.ephemeral_gb': 42}} self.instance = FauxInstance(**self.INSTANCE_PROPERTIES) self.instance.host = 'made-up-hostname' @@ -93,6 +96,10 @@ class TestLocationMetadata(base.TestCase): assert actual == iprops['image']['id'] elif name == 'image_ref_url': assert actual == iprops['image']['links'][0]['href'] + elif name == 'user_metadata': + expected = iprops['metadata']['metering.autoscale.group'][:256] + self.assertEqual(actual['autoscale_group'], expected) + self.assertEqual(len(actual), 1) else: assert actual == iprops[name]