From b27ac819fb2b6cd44d41dbaf71ba72805c6fe7d2 Mon Sep 17 00:00:00 2001 From: Koert van der Veer Date: Tue, 6 May 2014 15:17:39 +0200 Subject: [PATCH] Implemented metering for Cinder's snapshots Cinder already publishes the required notifications, but Ceilometer wasn't recording them. This patch fixes that. Closes-Bug: #1316670 Change-Id: I53532d1833db2f22a803bf77e548f5d3b095a30f --- ceilometer/tests/volume/test_notifications.py | 81 ++++++++++++++++--- ceilometer/volume/notifications.py | 61 +++++++++++--- doc/source/measurements.rst | 14 ++-- setup.cfg | 2 + 4 files changed, 131 insertions(+), 27 deletions(-) diff --git a/ceilometer/tests/volume/test_notifications.py b/ceilometer/tests/volume/test_notifications.py index 9602fd71..6779d0f6 100644 --- a/ceilometer/tests/volume/test_notifications.py +++ b/ceilometer/tests/volume/test_notifications.py @@ -45,7 +45,6 @@ NOTIFICATION_VOLUME_EXISTS = { u'priority': u'INFO' } - NOTIFICATION_VOLUME_DELETE = { u'_context_roles': [u'Member', u'admin'], u'_context_request_id': u'req-6ba8ccb4-1093-4a39-b029-adfaa3fc7ceb', @@ -102,9 +101,39 @@ NOTIFICATION_VOLUME_RESIZE = { u'priority': u'INFO'} +NOTIFICATION_SNAPSHOT_EXISTS = { + u'_context_roles': [u'admin'], + u'_context_request_id': u'req-7ef29a5d-adeb-48a8-b104-59c05361aa27', + u'_context_quota_class': None, + u'event_type': u'snapshot.exists', + u'timestamp': u'2012-09-21 09:29:10.620731', + u'message_id': u'e0e6a5ad-2fc9-453c-b3fb-03fe504538dc', + u'_context_auth_token': None, + u'_context_is_admin': True, + u'_context_project_id': None, + u'_context_timestamp': u'2012-09-21T09:29:10.266928', + u'_context_read_deleted': u'no', + u'_context_user_id': None, + u'_context_remote_address': None, + u'publisher_id': u'volume.ubuntu-VirtualBox', + u"payload": {u"audit_period_beginning": u"2014-05-06 11:00:00", + u"audit_period_ending": u"2014-05-06 12:00:00", + u"availability_zone": u"left", + u"created_at": u"2014-05-06 09:33:43", + u"deleted": u"", + u"display_name": "lil snapshot", + u"snapshot_id": u"dd163129-9476-4cf5-9311-dd425324d8d8", + u"status": u"available", + u"tenant_id": u"compliance", + u"user_id": u"e0271f64847b49429bb304c775c7427a", + u"volume_id": u"b96e026e-c9bf-4418-8d6f-4ba493bbb7d6", + u"volume_size": 3}, + u'priority': u'INFO'} + + class TestNotifications(test.BaseTestCase): - def _verify_common_sample(self, s, name, notification): + def _verify_common_sample_volume(self, s, name, notification): self.assertIsNotNone(s) self.assertEqual(s.name, name) self.assertEqual(notification['payload']['volume_id'], s.resource_id) @@ -117,7 +146,8 @@ class TestNotifications(test.BaseTestCase): samples = list(v.process_notification(NOTIFICATION_VOLUME_EXISTS)) self.assertEqual(1, len(samples)) s = samples[0] - self._verify_common_sample(s, 'volume', NOTIFICATION_VOLUME_EXISTS) + self._verify_common_sample_volume( + s, 'volume', NOTIFICATION_VOLUME_EXISTS) self.assertEqual(1, s.volume) def test_volume_size_exists(self): @@ -125,8 +155,8 @@ class TestNotifications(test.BaseTestCase): samples = list(v.process_notification(NOTIFICATION_VOLUME_EXISTS)) self.assertEqual(1, len(samples)) s = samples[0] - self._verify_common_sample(s, 'volume.size', - NOTIFICATION_VOLUME_EXISTS) + self._verify_common_sample_volume(s, 'volume.size', + NOTIFICATION_VOLUME_EXISTS) self.assertEqual(NOTIFICATION_VOLUME_EXISTS['payload']['size'], s.volume) @@ -135,7 +165,8 @@ class TestNotifications(test.BaseTestCase): samples = list(v.process_notification(NOTIFICATION_VOLUME_DELETE)) self.assertEqual(1, len(samples)) s = samples[0] - self._verify_common_sample(s, 'volume', NOTIFICATION_VOLUME_DELETE) + self._verify_common_sample_volume( + s, 'volume', NOTIFICATION_VOLUME_DELETE) self.assertEqual(1, s.volume) def test_volume_size_delete(self): @@ -143,8 +174,8 @@ class TestNotifications(test.BaseTestCase): samples = list(v.process_notification(NOTIFICATION_VOLUME_DELETE)) self.assertEqual(1, len(samples)) s = samples[0] - self._verify_common_sample(s, 'volume.size', - NOTIFICATION_VOLUME_DELETE) + self._verify_common_sample_volume(s, 'volume.size', + NOTIFICATION_VOLUME_DELETE) self.assertEqual(NOTIFICATION_VOLUME_DELETE['payload']['size'], s.volume) @@ -153,7 +184,8 @@ class TestNotifications(test.BaseTestCase): samples = list(v.process_notification(NOTIFICATION_VOLUME_RESIZE)) self.assertEqual(1, len(samples)) s = samples[0] - self._verify_common_sample(s, 'volume', NOTIFICATION_VOLUME_RESIZE) + self._verify_common_sample_volume( + s, 'volume', NOTIFICATION_VOLUME_RESIZE) self.assertEqual(1, s.volume) def test_volume_size_resize(self): @@ -161,7 +193,34 @@ class TestNotifications(test.BaseTestCase): samples = list(v.process_notification(NOTIFICATION_VOLUME_RESIZE)) self.assertEqual(1, len(samples)) s = samples[0] - self._verify_common_sample(s, 'volume.size', - NOTIFICATION_VOLUME_RESIZE) + self._verify_common_sample_volume(s, 'volume.size', + NOTIFICATION_VOLUME_RESIZE) self.assertEqual(NOTIFICATION_VOLUME_RESIZE['payload']['size'], s.volume) + + def _verify_common_sample_snapshot(self, s, name, notification): + self.assertIsNotNone(s) + self.assertEqual(name, s.name) + self.assertEqual(notification['payload']['snapshot_id'], s.resource_id) + self.assertEqual(notification['timestamp'], s.timestamp) + metadata = s.resource_metadata + self.assertEqual(notification['publisher_id'], metadata.get('host')) + + def test_snapshot_exists(self): + v = notifications.Snapshot(mock.Mock()) + samples = list(v.process_notification(NOTIFICATION_SNAPSHOT_EXISTS)) + self.assertEqual(1, len(samples)) + s = samples[0] + self._verify_common_sample_snapshot(s, 'snapshot', + NOTIFICATION_SNAPSHOT_EXISTS) + self.assertEqual(1, s.volume) + + def test_snapshot_size_exists(self): + v = notifications.SnapshotSize(mock.Mock()) + samples = list(v.process_notification(NOTIFICATION_SNAPSHOT_EXISTS)) + self.assertEqual(1, len(samples)) + s = samples[0] + self._verify_common_sample_snapshot(s, 'snapshot.size', + NOTIFICATION_SNAPSHOT_EXISTS) + volume_size = NOTIFICATION_SNAPSHOT_EXISTS['payload']['volume_size'] + self.assertEqual(volume_size, s.volume) diff --git a/ceilometer/volume/notifications.py b/ceilometer/volume/notifications.py index 79bd7063..2a0fdfee 100644 --- a/ceilometer/volume/notifications.py +++ b/ceilometer/volume/notifications.py @@ -37,14 +37,7 @@ cfg.CONF.register_opts(OPTS) class _Base(plugin.NotificationBase): - """Convert volume notifications into Counters.""" - - event_types = [ - 'volume.exists', - 'volume.create.*', - 'volume.delete.*', - 'volume.resize.*', - ] + """Convert volume/snapshot notification into Counters.""" @staticmethod def get_targets(conf): @@ -56,7 +49,18 @@ class _Base(plugin.NotificationBase): for topic in conf.notification_topics] -class Volume(_Base): +class _VolumeBase(_Base): + """Convert volume notifications into Counters.""" + + event_types = [ + 'volume.exists', + 'volume.create.*', + 'volume.delete.*', + 'volume.resize.*', + ] + + +class Volume(_VolumeBase): def process_notification(self, message): yield sample.Sample.from_notification( name='volume', @@ -69,7 +73,7 @@ class Volume(_Base): message=message) -class VolumeSize(_Base): +class VolumeSize(_VolumeBase): def process_notification(self, message): yield sample.Sample.from_notification( name='volume.size', @@ -80,3 +84,40 @@ class VolumeSize(_Base): project_id=message['payload']['tenant_id'], resource_id=message['payload']['volume_id'], message=message) + + +class _SnapshotBase(_Base): + """Convert snapshot notifications into Counters.""" + + event_types = [ + 'snapshot.exists', + 'snapshot.create.*', + 'snapshot.delete.*', + 'snapshot.resize.*', + ] + + +class Snapshot(_SnapshotBase): + def process_notification(self, message): + yield sample.Sample.from_notification( + name='snapshot', + type=sample.TYPE_GAUGE, + unit='snapshot', + volume=1, + user_id=message['payload']['user_id'], + project_id=message['payload']['tenant_id'], + resource_id=message['payload']['snapshot_id'], + message=message) + + +class SnapshotSize(_SnapshotBase): + def process_notification(self, message): + yield sample.Sample.from_notification( + name='snapshot.size', + type=sample.TYPE_GAUGE, + unit='GB', + volume=message['payload']['volume_size'], + user_id=message['payload']['user_id'], + project_id=message['payload']['tenant_id'], + resource_id=message['payload']['snapshot_id'], + message=message) diff --git a/doc/source/measurements.rst b/doc/source/measurements.rst index 80df5a78..7e3a80b0 100644 --- a/doc/source/measurements.rst +++ b/doc/source/measurements.rst @@ -176,12 +176,14 @@ image.serve Delta B image ID notification Image is Volume (Cinder) =============== -======================== ========== ======= ======== ============ ======================================================= -Name Type Unit Resource Origin Note -======================== ========== ======= ======== ============ ======================================================= -volume Gauge volume vol ID notification Existence of volume -volume.size Gauge GB vol ID notification Size of volume -======================== ========== ======= ======== ============ ======================================================= +======================== ========== ======== ======== ============ ======================================================= +Name Type Unit Resource Origin Note +======================== ========== ======== ======== ============ ======================================================= +volume Gauge volume vol ID notification Existence of volume +volume.size Gauge GB vol ID notification Size of volume +snapshot Gauge snapshot snap ID notification Existence of snapshot +snapshot.size Gauge GB snap ID notification Size of snapshot's volume +======================== ========== ======== ======== ============ ======================================================= Make sure Cinder is properly configured first: see :ref:`installing_manually`. diff --git a/setup.cfg b/setup.cfg index 5e470b8c..10576a5c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -49,6 +49,8 @@ ceilometer.notification = cpu_percent = ceilometer.compute.notifications.cpu:CpuPercent volume = ceilometer.volume.notifications:Volume volume_size = ceilometer.volume.notifications:VolumeSize + snapshot = ceilometer.volume.notifications:Snapshot + snapshot_size = ceilometer.volume.notifications:SnapshotSize image_crud = ceilometer.image.notifications:ImageCRUD image = ceilometer.image.notifications:Image image_size = ceilometer.image.notifications:ImageSize