diff --git a/ceilometer/image/notifications.py b/ceilometer/image/notifications.py new file mode 100644 index 00000000..67451352 --- /dev/null +++ b/ceilometer/image/notifications.py @@ -0,0 +1,78 @@ +# -*- encoding: utf-8 -*- +# +# Copyright © 2012 Red Hat, Inc +# +# Author: Eoghan Glynn +# +# 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. +"""Handler for producing image counter messages from glance notification + events. +""" + +from ceilometer import counter +from ceilometer import plugin + + +class ImageBase(plugin.NotificationBase): + """ + Listen for image.send notifications in order to mediate with + the metering framework. + """ + + @staticmethod + def get_event_types(): + return ['image.send'] + + def _counter(self, message, name, user_id, project_id): + metadata = self.notification_to_metadata(message) + return counter.Counter( + source='?', + name=name, + type='absolute', + volume=message['payload']['bytes_sent'], + resource_id=message['payload']['image_id'], + user_id=user_id, + project_id=project_id, + timestamp=message['timestamp'], + duration=0, + resource_metadata=metadata, + ) + + +class ImageDownload(ImageBase): + """ Emit image_download counter when an image is downloaded. """ + + metadata_keys = ['destination_ip', 'owner_id'] + + def process_notification(self, message): + return [ + self._counter(message, + 'image_download', + message['payload']['receiver_user_id'], + message['payload']['receiver_tenant_id']), + ] + + +class ImageServe(ImageBase): + """ Emit image_serve counter when an image is served out. """ + + metadata_keys = ['destination_ip', 'receiver_user_id', + 'receiver_tenant_id'] + + def process_notification(self, message): + return [ + self._counter(message, + 'image_serve', + message['payload']['owner_id'], + None), + ] diff --git a/setup.py b/setup.py index 2143e868..816d03d0 100755 --- a/setup.py +++ b/setup.py @@ -54,6 +54,8 @@ setuptools.setup( ephemeral_disk_size = ceilometer.compute.notifications:EphemeralDiskSize volume = ceilometer.volume.notifications:Volume volume_size = ceilometer.volume.notifications:VolumeSize + image_download = ceilometer.image.notifications:ImageDownload + image_serve = ceilometer.image.notifications:ImageServe [ceilometer.poll.compute] libvirt_diskio = ceilometer.compute.libvirt:DiskIOPollster diff --git a/tests/image/test_notifications.py b/tests/image/test_notifications.py new file mode 100644 index 00000000..4262eed0 --- /dev/null +++ b/tests/image/test_notifications.py @@ -0,0 +1,78 @@ +# -*- encoding: utf-8 -*- +# +# Copyright © 2012 Red Hat Inc. +# +# Author: Eoghan Glynn +# +# 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 datetime import datetime +import unittest + +from ceilometer.image import notifications +from tests import utils + +NOW = datetime.isoformat(datetime.utcnow()) + +NOTIFICATION_IMAGE_SEND = { + u'event_type': u'image.send', + u'timestamp': NOW, + u'message_id': utils.fake_uuid('a'), + u'priority': u'INFO', + u'publisher_id': u'images.example.com', + u'payload': {u'receiver_tenant_id': utils.fake_uuid('b'), + u'destination_ip': u'1.2.3.4', + u'bytes_sent': 42, + u'image_id': utils.fake_uuid('c'), + u'receiver_user_id': utils.fake_uuid('d'), + u'owner_id': utils.fake_uuid('e')} +} + + +class TestNotification(unittest.TestCase): + + def _verify_common_counter(self, counter, name): + self.assertFalse(counter is None) + self.assertEqual(counter.name, name) + self.assertEqual(counter.type, 'absolute') + self.assertEqual(counter.volume, 42) + self.assertEqual(counter.resource_id, utils.fake_uuid('c')) + self.assertEqual(counter.timestamp, NOW) + self.assertEqual(counter.duration, 0) + metadata = counter.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) + self.assertEqual(len(counters), 1) + download = counters[0] + self._verify_common_counter(download, 'image_download') + 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')) + + def test_image_serve(self): + handler = notifications.ImageServe() + counters = handler.process_notification(NOTIFICATION_IMAGE_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.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')) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 00000000..f019f14d --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,21 @@ +# -*- encoding: utf-8 -*- +# +# Copyright © 2012 Red Hat Inc. +# +# Author: Eoghan Glynn +# +# 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. + + +def fake_uuid(x): + return '%s-%s-%s-%s' % (x * 8, x * 4, x * 4, x * 12)