summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ceilometer/compute/notifications.py51
-rw-r--r--ceilometer/compute/pollsters/util.py34
-rw-r--r--ceilometer/compute/util.py49
-rw-r--r--etc/ceilometer/ceilometer.conf.sample2
-rw-r--r--tests/compute/test_notifications.py248
5 files changed, 300 insertions, 84 deletions
diff --git a/ceilometer/compute/notifications.py b/ceilometer/compute/notifications.py
index 5b59da27..2e243074 100644
--- a/ceilometer/compute/notifications.py
+++ b/ceilometer/compute/notifications.py
@@ -20,9 +20,13 @@
"""Converters for producing compute sample messages from notification events.
"""
+import abc
+import six
+
from oslo.config import cfg
from ceilometer import sample
+from ceilometer.compute import util
from ceilometer import plugin
@@ -50,10 +54,36 @@ class ComputeNotificationBase(plugin.NotificationBase):
]
-class InstanceScheduled(ComputeNotificationBase):
- event_types = ['scheduler.run_instance.scheduled']
+@six.add_metaclass(abc.ABCMeta)
+class UserMetadataAwareInstanceNotificationBase(ComputeNotificationBase):
+ """Consumes notifications containing instance user metadata.
+ """
def process_notification(self, message):
+ instance_properties = self.get_instance_properties(message)
+ if 'metadata' in instance_properties:
+ src_metadata = instance_properties['metadata']
+ del instance_properties['metadata']
+ util.add_reserved_user_metadata(src_metadata, instance_properties)
+ return self.get_sample(message)
+
+ def get_instance_properties(self, message):
+ """Retrieve instance properties from notification payload."""
+ return message['payload']
+
+ @abc.abstractmethod
+ def get_sample(self, message):
+ """Derive sample from notification payload."""
+
+
+class InstanceScheduled(UserMetadataAwareInstanceNotificationBase):
+ event_types = ['scheduler.run_instance.scheduled']
+
+ def get_instance_properties(self, message):
+ """Retrieve instance properties from notification payload."""
+ return message['payload']['request_spec']['instance_properties']
+
+ def get_sample(self, message):
yield sample.Sample.from_notification(
name='instance.scheduled',
type=sample.TYPE_DELTA,
@@ -67,14 +97,15 @@ class InstanceScheduled(ComputeNotificationBase):
message=message)
-class ComputeInstanceNotificationBase(ComputeNotificationBase):
+class ComputeInstanceNotificationBase(
+ UserMetadataAwareInstanceNotificationBase):
"""Convert compute.instance.* notifications into Samples
"""
event_types = ['compute.instance.*']
class Instance(ComputeInstanceNotificationBase):
- def process_notification(self, message):
+ def get_sample(self, message):
yield sample.Sample.from_notification(
name='instance',
type=sample.TYPE_GAUGE,
@@ -87,7 +118,7 @@ class Instance(ComputeInstanceNotificationBase):
class Memory(ComputeInstanceNotificationBase):
- def process_notification(self, message):
+ def get_sample(self, message):
yield sample.Sample.from_notification(
name='memory',
type=sample.TYPE_GAUGE,
@@ -100,7 +131,7 @@ class Memory(ComputeInstanceNotificationBase):
class VCpus(ComputeInstanceNotificationBase):
- def process_notification(self, message):
+ def get_sample(self, message):
yield sample.Sample.from_notification(
name='vcpus',
type=sample.TYPE_GAUGE,
@@ -113,7 +144,7 @@ class VCpus(ComputeInstanceNotificationBase):
class RootDiskSize(ComputeInstanceNotificationBase):
- def process_notification(self, message):
+ def get_sample(self, message):
yield sample.Sample.from_notification(
name='disk.root.size',
type=sample.TYPE_GAUGE,
@@ -126,7 +157,7 @@ class RootDiskSize(ComputeInstanceNotificationBase):
class EphemeralDiskSize(ComputeInstanceNotificationBase):
- def process_notification(self, message):
+ def get_sample(self, message):
yield sample.Sample.from_notification(
name='disk.ephemeral.size',
type=sample.TYPE_GAUGE,
@@ -139,7 +170,7 @@ class EphemeralDiskSize(ComputeInstanceNotificationBase):
class InstanceFlavor(ComputeInstanceNotificationBase):
- def process_notification(self, message):
+ def get_sample(self, message):
instance_type = message.get('payload', {}).get('instance_type')
if instance_type:
yield sample.Sample.from_notification(
@@ -160,7 +191,7 @@ class InstanceDelete(ComputeInstanceNotificationBase):
event_types = ['compute.instance.delete.samples']
- def process_notification(self, message):
+ def get_sample(self, message):
for s in message['payload'].get('samples', []):
yield sample.Sample.from_notification(
name=s['name'],
diff --git a/ceilometer/compute/pollsters/util.py b/ceilometer/compute/pollsters/util.py
index b8d84977..7b7f9af8 100644
--- a/ceilometer/compute/pollsters/util.py
+++ b/ceilometer/compute/pollsters/util.py
@@ -18,8 +18,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from oslo.config import cfg
-
+from ceilometer.compute import util as compute_util
from ceilometer import sample
from ceilometer.openstack.common import timeutils
@@ -35,35 +34,6 @@ INSTANCE_PROPERTIES = [
'ramdisk_id',
]
-OPTS = [
- cfg.ListOpt('reserved_metadata_namespace',
- default=['metering.'],
- help='list of metadata prefixes reserved for metering use'),
- cfg.IntOpt('reserved_metadata_length',
- default=256,
- help='limit on length of reserved metadata values'),
-]
-
-cfg.CONF.register_opts(OPTS)
-
-
-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.
@@ -97,7 +67,7 @@ def _get_metadata_from_object(instance):
metadata['root_gb'] = int(metadata['disk_gb']) - \
int(metadata['ephemeral_gb'])
- return _add_reserved_user_metadata(instance, metadata)
+ return compute_util.add_reserved_user_metadata(instance.metadata, metadata)
def make_sample_from_instance(instance, name, type, unit, volume,
diff --git a/ceilometer/compute/util.py b/ceilometer/compute/util.py
new file mode 100644
index 00000000..ba142b45
--- /dev/null
+++ b/ceilometer/compute/util.py
@@ -0,0 +1,49 @@
+# -*- encoding: utf-8 -*-
+#
+# Copyright © 2014 Red Hat, Inc
+#
+# Author: Eoghan Glynn <eglynn@redhat.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo.config import cfg
+
+
+OPTS = [
+ cfg.ListOpt('reserved_metadata_namespace',
+ default=['metering.'],
+ help='List of metadata prefixes reserved for metering use.'),
+ cfg.IntOpt('reserved_metadata_length',
+ default=256,
+ help='Limit on length of reserved metadata values.'),
+]
+
+cfg.CONF.register_opts(OPTS)
+
+
+def add_reserved_user_metadata(src_metadata, dest_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 src_metadata.items()
+ if (k.startswith(prefix) and
+ k[len(prefix):].replace('.', '_') not in dest_metadata)
+ )
+ user_metadata.update(md)
+ if user_metadata:
+ dest_metadata['user_metadata'] = user_metadata
+
+ return dest_metadata
diff --git a/etc/ceilometer/ceilometer.conf.sample b/etc/ceilometer/ceilometer.conf.sample
index 6b0ee20c..c514d2d3 100644
--- a/etc/ceilometer/ceilometer.conf.sample
+++ b/etc/ceilometer/ceilometer.conf.sample
@@ -48,7 +48,7 @@
#
-# Options defined in ceilometer.compute.pollsters.util
+# Options defined in ceilometer.compute.util
#
# list of metadata prefixes reserved for metering use (list
diff --git a/tests/compute/test_notifications.py b/tests/compute/test_notifications.py
index d341b3b4..c7b4bee5 100644
--- a/tests/compute/test_notifications.py
+++ b/tests/compute/test_notifications.py
@@ -201,7 +201,9 @@ INSTANCE_FINISH_RESIZE_END = {
u'hostname': u's1',
u'state': u'resized',
u'launched_at': u'2013-01-04T15:10:14.923939',
- u'metadata': [],
+ u'metadata': {u'metering.server_group': u'Group_A',
+ u'AutoScalingGroupName': u'tyky-Group_Awste7',
+ u'metering.foo.bar': u'true'},
u'ramdisk_id': u'5f23128e-5525-46d8-bc66-9c30cd87141a',
u'access_ip_v6': None,
u'disk_gb': 20,
@@ -258,7 +260,9 @@ INSTANCE_RESIZE_REVERT_END = {
u'hostname': u's1',
u'state': u'resized',
u'launched_at': u'2013-01-04T15:10:14.000000',
- u'metadata': [],
+ u'metadata': {u'metering.server_group': u'Group_A',
+ u'AutoScalingGroupName': u'tyky-Group_A-wste7',
+ u'metering.foo.bar': u'true'},
u'ramdisk_id': u'5f23128e-5525-46d8-bc66-9c30cd87141a',
u'access_ip_v6': None,
u'disk_gb': 0,
@@ -315,7 +319,9 @@ INSTANCE_DELETE_SAMPLES = {
u'hostname': u's1',
u'state': u'resized',
u'launched_at': u'2013-01-04T15:10:14.000000',
- u'metadata': [],
+ u'metadata': {u'metering.server_group': u'Group_A',
+ u'AutoScalingGroupName': u'tyky-Group_A-wste7',
+ u'metering.foo.bar': u'true'},
u'ramdisk_id': u'5f23128e-5525-46d8-bc66-9c30cd87141a',
u'access_ip_v6': None,
u'disk_gb': 0,
@@ -352,49 +358,190 @@ INSTANCE_DELETE_SAMPLES = {
u'priority': u'INFO'
}
-
INSTANCE_SCHEDULED = {
- u'_context_roles': [u'admin'],
- u'_context_request_id': u'req-9da1d714-dabe-42fd-8baa-583e57cd4f1a',
+ u'_context_request_id': u'req-f28a836a-32bf-4cc3-940a-3515878c181f',
u'_context_quota_class': None,
u'event_type': u'scheduler.run_instance.scheduled',
- u'_context_user_name': u'admin',
- u'_context_project_name': u'admin',
- u'timestamp': u'2013-01-04 15:20:32.009532',
- u'_context_is_admin': True,
- u'message_id': u'c48deeba-d0c3-4154-b3db-47480b52267a',
- u'_context_auth_token': None,
- u'_context_instance_lock_checked': False,
- u'_context_project_id': u'cea4b25edb484e5392727181b7721d29',
- u'_context_timestamp': u'2013-01-04T15:19:51.018218',
- u'_context_read_deleted': u'no',
- u'_context_user_id': u'01b83a5e23f24a6fb6cd073c0aee6eed',
- u'_context_remote_address': u'10.147.132.184',
- u'publisher_id': u'compute.ip-10-147-132-184.ec2.internal',
+ u'_context_service_catalog': [{
+ u'endpoints': [{
+ u'adminURL':
+ u'http://172.16.12.21:8776/v1/2bd766a095b44486bf07cf7f666997eb',
+ u'region': u'RegionOne',
+ u'internalURL':
+ u'http://172.16.12.21:8776/v1/2bd766a095b44486bf07cf7f666997eb',
+ u'id': u'30cb904fdc294eea9b225e06b2d0d4eb',
+ u'publicURL':
+ u'http://172.16.12.21:8776/v1/2bd766a095b44486bf07cf7f666997eb'}],
+ u'endpoints_links': [],
+ u'type': u'volume',
+ u'name': u'cinder'}],
+ u'_context_auth_token': u'TOK',
+ u'_context_user_id': u'0a757cd896b64b65ba3784afef564116',
u'payload': {
'instance_id': 'fake-uuid1-1',
- 'weighted_host': {
- 'host': 'host3',
- 'weight': 3.0,
- },
- 'request_spec': {
- 'instance_properties': {
- 'root_gb': 512,
- 'ephemeral_gb': 0,
- 'launch_index': 0,
- 'memory_mb': 512,
- 'vcpus': 1,
- 'os_type': 'Linux',
- 'project_id': 1,
- 'system_metadata': {'system': 'metadata'}},
- 'instance_type': {'memory_mb': 512,
- 'vcpus': 1,
- 'root_gb': 512,
- 'ephemeral_gb': 0},
- 'instance_uuids': ['fake-uuid1-1'],
- },
- },
- u'priority': u'INFO'
+ u'weighted_host': {u'host': u'eglynn-f19-devstack3', u'weight': 1.0},
+ u'request_spec': {
+ u'num_instances': 1,
+ u'block_device_mapping': [{
+ u'instance_uuid': u'9206baae-c3b6-41bc-96f2-2c0726ff51c8',
+ u'guest_format': None,
+ u'boot_index': 0,
+ u'no_device': None,
+ u'connection_info': None,
+ u'volume_id': None,
+ u'volume_size': None,
+ u'device_name': None,
+ u'disk_bus': None,
+ u'image_id': u'0560ac3f-3bcd-434d-b012-8dd7a212b73b',
+ u'source_type': u'image',
+ u'device_type': u'disk',
+ u'snapshot_id': None,
+ u'destination_type': u'local',
+ u'delete_on_termination': True}],
+ u'image': {
+ u'status': u'active',
+ u'name': u'cirros-0.3.1-x86_64-uec',
+ u'deleted': False,
+ u'container_format': u'ami',
+ u'created_at': u'2014-02-18T13:16:26.000000',
+ u'disk_format': u'ami',
+ u'updated_at': u'2014-02-18T13:16:27.000000',
+ u'properties': {
+ u'kernel_id': u'c8794c1a-4158-42cc-9f97-d0d250c9c6a4',
+ u'ramdisk_id': u'4999726c-545c-4a9e-bfc0-917459784275'},
+ u'min_disk': 0,
+ u'min_ram': 0,
+ u'checksum': u'f8a2eeee2dc65b3d9b6e63678955bd83',
+ u'owner': u'2bd766a095b44486bf07cf7f666997eb',
+ u'is_public': True,
+ u'deleted_at': None,
+ u'id': u'0560ac3f-3bcd-434d-b012-8dd7a212b73b',
+ u'size': 25165824},
+ u'instance_type': {
+ u'root_gb': 1,
+ u'name': u'm1.tiny',
+ u'ephemeral_gb': 0,
+ u'memory_mb': 512,
+ u'vcpus': 1,
+ u'extra_specs': {},
+ u'swap': 0,
+ u'rxtx_factor': 1.0,
+ u'flavorid': u'1',
+ u'vcpu_weight': None,
+ u'id': 2},
+ u'instance_properties': {
+ u'vm_state': u'building',
+ u'availability_zone': None,
+ u'terminated_at': None,
+ u'ephemeral_gb': 0,
+ u'instance_type_id': 2,
+ u'user_data': None,
+ u'cleaned': False,
+ u'vm_mode': None,
+ u'deleted_at': None,
+ u'reservation_id': u'r-ven5q6om',
+ u'id': 15,
+ u'security_groups': [{
+ u'deleted_at': None,
+ u'user_id': u'0a757cd896b64b65ba3784afef564116',
+ u'description': u'default',
+ u'deleted': False,
+ u'created_at': u'2014-02-19T11:02:31.000000',
+ u'updated_at': None,
+ u'project_id': u'2bd766a095b44486bf07cf7f666997eb',
+ u'id': 1,
+ u'name': u'default'}],
+ u'disable_terminate': False,
+ u'root_device_name': None,
+ u'display_name': u'new',
+ u'uuid': u'9206baae-c3b6-41bc-96f2-2c0726ff51c8',
+ u'default_swap_device': None,
+ u'info_cache': {
+ u'instance_uuid': u'9206baae-c3b6-41bc-96f2-2c0726ff51c8',
+ u'deleted': False,
+ u'created_at': u'2014-03-05T12:44:00.000000',
+ u'updated_at': None,
+ u'network_info': [],
+ u'deleted_at': None},
+ u'hostname': u'new',
+ u'launched_on': None,
+ u'display_description': u'new',
+ u'key_data': None,
+ u'deleted': False,
+ u'config_drive': u'',
+ u'power_state': 0,
+ u'default_ephemeral_device': None,
+ u'progress': 0,
+ u'project_id': u'2bd766a095b44486bf07cf7f666997eb',
+ u'launched_at': None,
+ u'scheduled_at': None,
+ u'node': None,
+ u'ramdisk_id': u'4999726c-545c-4a9e-bfc0-917459784275',
+ u'access_ip_v6': None,
+ u'access_ip_v4': None,
+ u'kernel_id': u'c8794c1a-4158-42cc-9f97-d0d250c9c6a4',
+ u'key_name': None,
+ u'updated_at': None,
+ u'host': None,
+ u'root_gb': 1,
+ u'user_id': u'0a757cd896b64b65ba3784afef564116',
+ u'system_metadata': {
+ u'image_kernel_id':
+ u'c8794c1a-4158-42cc-9f97-d0d250c9c6a4',
+ u'image_min_disk': u'1',
+ u'instance_type_memory_mb': u'512',
+ u'instance_type_swap': u'0',
+ u'instance_type_vcpu_weight': None,
+ u'instance_type_root_gb': u'1',
+ u'instance_type_name': u'm1.tiny',
+ u'image_ramdisk_id':
+ u'4999726c-545c-4a9e-bfc0-917459784275',
+ u'instance_type_id': u'2',
+ u'instance_type_ephemeral_gb': u'0',
+ u'instance_type_rxtx_factor': u'1.0',
+ u'instance_type_flavorid': u'1',
+ u'instance_type_vcpus': u'1',
+ u'image_container_format': u'ami',
+ u'image_min_ram': u'0',
+ u'image_disk_format': u'ami',
+ u'image_base_image_ref':
+ u'0560ac3f-3bcd-434d-b012-8dd7a212b73b'},
+ u'task_state': u'scheduling',
+ u'shutdown_terminate': False,
+ u'cell_name': None,
+ u'ephemeral_key_uuid': None,
+ u'locked': False,
+ u'name': u'instance-0000000f',
+ u'created_at': u'2014-03-05T12:44:00.000000',
+ u'locked_by': None,
+ u'launch_index': 0,
+ u'memory_mb': 512,
+ u'vcpus': 1,
+ u'image_ref': u'0560ac3f-3bcd-434d-b012-8dd7a212b73b',
+ u'architecture': None,
+ u'auto_disk_config': False,
+ u'os_type': None,
+ u'metadata': {u'metering.server_group': u'Group_A',
+ u'AutoScalingGroupName': u'tyky-Group_Awste7',
+ u'metering.foo.bar': u'true'}},
+ u'security_group': [u'default'],
+ u'instance_uuids': [u'9206baae-c3b6-41bc-96f2-2c0726ff51c8']}},
+ u'priority': u'INFO',
+ u'_context_is_admin': True,
+ u'_context_timestamp': u'2014-03-05T12:44:00.135674',
+ u'publisher_id': u'scheduler.eglynn-f19-devstack3',
+ u'message_id': u'd6c1ae63-a26b-47c7-8397-8794216e09dd',
+ u'_context_remote_address': u'172.16.12.21',
+ u'_context_roles': [u'_member_', u'admin'],
+ u'timestamp': u'2014-03-05 12:44:00.733758',
+ u'_context_user': u'0a757cd896b64b65ba3784afef564116',
+ u'_unique_id': u'2af47cbdde604ff794bb046f3f9db1e2',
+ u'_context_project_name': u'admin',
+ u'_context_read_deleted': u'no',
+ u'_context_tenant': u'2bd766a095b44486bf07cf7f666997eb',
+ u'_context_instance_lock_checked': False,
+ u'_context_project_id': u'2bd766a095b44486bf07cf7f666997eb',
+ u'_context_user_name': u'admin'
}
@@ -424,6 +571,14 @@ class TestNotifications(base.TestCase):
def _find_counter(counters, name):
return filter(lambda counter: counter.name == name, counters)[0]
+ def _verify_user_metadata(self, metadata):
+ self.assertIn('user_metadata', metadata)
+ user_meta = metadata['user_metadata']
+ self.assertEqual(user_meta.get('server_group'), 'Group_A')
+ self.assertNotIn('AutoScalingGroupName', user_meta)
+ self.assertIn('foo_bar', user_meta)
+ self.assertNotIn('foo.bar', user_meta)
+
def test_instance_create_instance(self):
ic = notifications.Instance()
counters = list(ic.process_notification(INSTANCE_CREATE_END))
@@ -501,6 +656,7 @@ class TestNotifications(base.TestCase):
c = counters[0]
self.assertEqual(c.volume, 1)
self.assertEqual(c.name, 'instance:m1.small')
+ self._verify_user_metadata(c.resource_metadata)
def test_instance_finish_resize_memory(self):
ic = notifications.Memory()
@@ -509,6 +665,7 @@ class TestNotifications(base.TestCase):
c = counters[0]
self.assertEqual(c.volume,
INSTANCE_FINISH_RESIZE_END['payload']['memory_mb'])
+ self._verify_user_metadata(c.resource_metadata)
def test_instance_finish_resize_vcpus(self):
ic = notifications.VCpus()
@@ -524,6 +681,7 @@ class TestNotifications(base.TestCase):
self.assertEqual(len(counters), 1)
c = counters[0]
self.assertEqual(c.volume, 1)
+ self._verify_user_metadata(c.resource_metadata)
def test_instance_resize_finish_flavor(self):
ic = notifications.InstanceFlavor()
@@ -532,6 +690,7 @@ class TestNotifications(base.TestCase):
c = counters[0]
self.assertEqual(c.volume, 1)
self.assertEqual(c.name, 'instance:m1.tiny')
+ self._verify_user_metadata(c.resource_metadata)
def test_instance_resize_finish_memory(self):
ic = notifications.Memory()
@@ -540,6 +699,7 @@ class TestNotifications(base.TestCase):
c = counters[0]
self.assertEqual(c.volume,
INSTANCE_RESIZE_REVERT_END['payload']['memory_mb'])
+ self._verify_user_metadata(c.resource_metadata)
def test_instance_resize_finish_vcpus(self):
ic = notifications.VCpus()
@@ -548,6 +708,7 @@ class TestNotifications(base.TestCase):
c = counters[0]
self.assertEqual(c.volume,
INSTANCE_RESIZE_REVERT_END['payload']['vcpus'])
+ self._verify_user_metadata(c.resource_metadata)
def test_instance_delete_samples(self):
ic = notifications.InstanceDelete()
@@ -555,6 +716,8 @@ class TestNotifications(base.TestCase):
self.assertEqual(len(counters), 2)
names = [c.name for c in counters]
self.assertEqual(names, ['sample-name1', 'sample-name2'])
+ c = counters[0]
+ self._verify_user_metadata(c.resource_metadata)
def test_instance_scheduled(self):
ic = notifications.InstanceScheduled()
@@ -568,3 +731,6 @@ class TestNotifications(base.TestCase):
self.assertEqual(names, ['instance.scheduled'])
rid = [c.resource_id for c in counters]
self.assertEqual(rid, ['fake-uuid1-1'])
+ c = counters[0]
+ metadata = c.resource_metadata['request_spec']['instance_properties']
+ self._verify_user_metadata(metadata)