diff --git a/ceilometer/image/glance.py b/ceilometer/image/glance.py index 75e545f0..3125c4c6 100644 --- a/ceilometer/image/glance.py +++ b/ceilometer/image/glance.py @@ -109,7 +109,7 @@ class ImageSizePollster(_Base): for image in self.iter_images(): yield counter.Counter( source='?', - name='image_size', + name='image.size', type=counter.TYPE_GAUGE, volume=image['size'], user_id=None, diff --git a/ceilometer/image/notifications.py b/ceilometer/image/notifications.py index b9bcc389..73b15778 100644 --- a/ceilometer/image/notifications.py +++ b/ceilometer/image/notifications.py @@ -41,10 +41,6 @@ class ImageBase(plugin.NotificationBase): the metering framework. """ - @staticmethod - def get_event_types(): - return ['image.send'] - @staticmethod def get_exchange_topics(conf): """Return a sequence of ExchangeTopics defining the exchange and @@ -56,19 +52,89 @@ class ImageBase(plugin.NotificationBase): for topic in conf.notification_topics)), ] - def _counter(self, message, name, user_id, project_id): + +class ImageCRUDBase(ImageBase): + + metadata_keys = [ + 'name', + 'size', + 'status', + 'disk_format', + 'container_format', + 'location', + 'deleted', + 'created_at', + 'updated_at', + 'properties', + 'protected', + 'checksum', + 'is_public', + 'deleted_at', + 'min_ram', + ] + + @staticmethod + def get_event_types(): + return [ + 'image.update', + ] + + +class ImageCRUD(ImageCRUDBase): + + def process_notification(self, message): metadata = self.notification_to_metadata(message) - return counter.Counter( + return [ + counter.Counter( source='?', - name=name, - type=counter.TYPE_GAUGE, - volume=message['payload']['bytes_sent'], - resource_id=message['payload']['image_id'], - user_id=user_id, - project_id=project_id, + name=message['event_type'], + type=counter.TYPE_DELTA, + volume=1, + resource_id=message['payload']['id'], + user_id=None, + project_id=message['payload']['owner'], timestamp=message['timestamp'], resource_metadata=metadata, - ) + ), + ] + + +class Image(ImageCRUDBase): + + def process_notification(self, message): + metadata = self.notification_to_metadata(message) + return [ + counter.Counter( + source='?', + name='image', + type=counter.TYPE_GAUGE, + volume=1, + resource_id=message['payload']['id'], + user_id=None, + project_id=message['payload']['owner'], + timestamp=message['timestamp'], + resource_metadata=metadata, + ), + ] + + +class ImageSize(ImageCRUDBase): + + def process_notification(self, message): + metadata = self.notification_to_metadata(message) + return [ + counter.Counter( + source='?', + name='image.size', + type=counter.TYPE_GAUGE, + volume=message['payload']['size'], + resource_id=message['payload']['id'], + user_id=None, + project_id=message['payload']['owner'], + timestamp=message['timestamp'], + resource_metadata=metadata, + ), + ] class ImageDownload(ImageBase): @@ -76,13 +142,27 @@ class ImageDownload(ImageBase): metadata_keys = ['destination_ip', 'owner_id'] - def process_notification(self, message): + @staticmethod + def get_event_types(): return [ - self._counter(message, - 'image_download', - message['payload']['receiver_user_id'], - message['payload']['receiver_tenant_id']), - ] + 'image.send', + ] + + def process_notification(self, message): + metadata = self.notification_to_metadata(message) + return [ + counter.Counter( + source='?', + name='image.download', + type=counter.TYPE_DELTA, + volume=message['payload']['bytes_sent'], + resource_id=message['payload']['image_id'], + user_id=message['payload']['receiver_user_id'], + project_id=message['payload']['receiver_tenant_id'], + timestamp=message['timestamp'], + resource_metadata=metadata, + ), + ] class ImageServe(ImageBase): @@ -91,10 +171,24 @@ class ImageServe(ImageBase): metadata_keys = ['destination_ip', 'receiver_user_id', 'receiver_tenant_id'] - def process_notification(self, message): + @staticmethod + def get_event_types(): return [ - self._counter(message, - 'image_serve', - message['payload']['owner_id'], - None), - ] + 'image.send', + ] + + def process_notification(self, message): + metadata = self.notification_to_metadata(message) + return [ + counter.Counter( + source='?', + name='image.serve', + type=counter.TYPE_DELTA, + volume=message['payload']['bytes_sent'], + resource_id=message['payload']['image_id'], + user_id=None, + project_id=message['payload']['owner_id'], + timestamp=message['timestamp'], + resource_metadata=metadata, + ), + ] diff --git a/doc/source/install.rst b/doc/source/install.rst index df6d79db..953339bb 100644 --- a/doc/source/install.rst +++ b/doc/source/install.rst @@ -61,6 +61,10 @@ ceilometer:: # Enable the ceilometer services enable_service ceilometer-acompute,ceilometer-acentral,ceilometer-collector,ceilometer-api +5. If you want to be able to retrieve image counters, you need to instruct + Glance to send notifications to the bus by changing ``notifier_strategy`` + to ``rabbit`` in ``glance-api.conf``. + Installing Manually +++++++++++++++++++ diff --git a/doc/source/measurements.rst b/doc/source/measurements.rst index 6b14b4a1..8a897839 100644 --- a/doc/source/measurements.rst +++ b/doc/source/measurements.rst @@ -82,9 +82,10 @@ Image (Glance) Name Type Volume Resource Note ======================== ========== ======= ======== ======================================================= image Gauge 1 image ID Image polling -> it (still) exists -image_size Gauge bytes image ID Uploaded image size -image_download Gauge bytes image ID Image is downloaded -image_serve Gauge bytes image ID Image is served out +image.size Gauge bytes image ID Uploaded image size +image.update Delta reqs image ID Number of update on the image +image.download Delta bytes image ID Image is downloaded +image.serve Delta bytes image ID Image is served out ======================== ========== ======= ======== ======================================================= Volume (Cinder) diff --git a/setup.py b/setup.py index 52a64085..21160cdf 100755 --- a/setup.py +++ b/setup.py @@ -55,6 +55,9 @@ setuptools.setup( ephemeral_disk_size = ceilometer.compute.notifications:EphemeralDiskSize volume = ceilometer.volume.notifications:Volume volume_size = ceilometer.volume.notifications:VolumeSize + image_crud = ceilometer.image.notifications:ImageCRUD + image = ceilometer.image.notifications:Image + image_size = ceilometer.image.notifications:ImageSize image_download = ceilometer.image.notifications:ImageDownload image_serve = ceilometer.image.notifications:ImageServe network = ceilometer.network.notifications:Network diff --git a/tests/image/test_notifications.py b/tests/image/test_notifications.py index 5eb74da5..9cc75101 100644 --- a/tests/image/test_notifications.py +++ b/tests/image/test_notifications.py @@ -3,6 +3,7 @@ # Copyright © 2012 Red Hat Inc. # # Author: Eoghan Glynn +# Author: Julien danjou # # 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 @@ -25,7 +26,7 @@ from tests import utils NOW = datetime.isoformat(datetime.utcnow()) -NOTIFICATION_IMAGE_SEND = { +NOTIFICATION_SEND = { u'event_type': u'image.send', u'timestamp': NOW, u'message_id': utils.fake_uuid('a'), @@ -39,40 +40,90 @@ NOTIFICATION_IMAGE_SEND = { u'owner_id': utils.fake_uuid('e')} } +IMAGE_META = {u'status': u'saving', + u'name': u'fake image #3', + u'deleted': False, + u'container_format': u'ovf', + u'created_at': u'2012-09-18T10:13:44.571370', + u'disk_format': u'vhd', + u'updated_at': u'2012-09-18T10:13:44.623120', + u'properties': {u'key2': u'value2', + u'key1': u'value1'}, + u'min_disk': 0, + u'protected': False, + u'id': utils.fake_uuid('c'), + u'location': None, + u'checksum': u'd990432ef91afef3ad9dbf4a975d3365', + u'owner': "fake", + u'is_public': False, + u'deleted_at': None, + u'min_ram': 0, + u'size': 19} + + +NOTIFICATION_UPDATE = {"message_id": "0c65cb9c-018c-11e2-bc91-5453ed1bbb5f", + "publisher_id": "images.example.com", + "event_type": "image.update", + "priority": "info", + "payload": IMAGE_META, + "timestamp": NOW} + class TestNotification(unittest.TestCase): - def _verify_common_counter(self, c, name): + def _verify_common_counter(self, c, name, volume): self.assertFalse(c is None) self.assertEqual(c.name, name) - self.assertEqual(c.type, counter.TYPE_GAUGE) - self.assertEqual(c.volume, 42) self.assertEqual(c.resource_id, utils.fake_uuid('c')) self.assertEqual(c.timestamp, NOW) + self.assertEqual(c.volume, volume) metadata = c.resource_metadata - self.assertEquals(metadata.get('event_type'), u'image.send') self.assertEquals(metadata.get('host'), u'images.example.com') - self.assertEquals(metadata.get('destination_ip'), u'1.2.3.4') def test_image_download(self): handler = notifications.ImageDownload() - counters = handler.process_notification(NOTIFICATION_IMAGE_SEND) + counters = handler.process_notification(NOTIFICATION_SEND) self.assertEqual(len(counters), 1) download = counters[0] - self._verify_common_counter(download, 'image_download') + self._verify_common_counter(download, 'image.download', 42) self.assertEqual(download.user_id, utils.fake_uuid('d')) self.assertEqual(download.project_id, utils.fake_uuid('b')) - self.assertEquals(download.resource_metadata.get('owner_id'), - utils.fake_uuid('e')) + self.assertEqual(download.type, counter.TYPE_DELTA) def test_image_serve(self): handler = notifications.ImageServe() - counters = handler.process_notification(NOTIFICATION_IMAGE_SEND) + counters = handler.process_notification(NOTIFICATION_SEND) self.assertEqual(len(counters), 1) serve = counters[0] - self._verify_common_counter(serve, 'image_serve') - self.assertEqual(serve.user_id, utils.fake_uuid('e')) + self._verify_common_counter(serve, 'image.serve', 42) + self.assertEqual(serve.project_id, utils.fake_uuid('e')) self.assertEquals(serve.resource_metadata.get('receiver_user_id'), utils.fake_uuid('d')) self.assertEquals(serve.resource_metadata.get('receiver_tenant_id'), utils.fake_uuid('b')) + self.assertEqual(serve.type, counter.TYPE_DELTA) + + def test_image_crud_on_update(self): + handler = notifications.ImageCRUD() + counters = handler.process_notification(NOTIFICATION_UPDATE) + self.assertEqual(len(counters), 1) + update = counters[0] + self._verify_common_counter(update, 'image.update', 1) + self.assertEqual(update.type, counter.TYPE_DELTA) + + def test_image_on_update(self): + handler = notifications.Image() + counters = handler.process_notification(NOTIFICATION_UPDATE) + self.assertEqual(len(counters), 1) + update = counters[0] + self._verify_common_counter(update, 'image', 1) + self.assertEqual(update.type, counter.TYPE_GAUGE) + + def test_image_size_on_update(self): + handler = notifications.ImageSize() + counters = handler.process_notification(NOTIFICATION_UPDATE) + self.assertEqual(len(counters), 1) + update = counters[0] + self._verify_common_counter(update, 'image.size', + IMAGE_META['size']) + self.assertEqual(update.type, counter.TYPE_GAUGE)