image: add update event, fix ImageServe owner

This adds handling of image.update events and create 2 counters for it:
Image, counting images, and ImageSize, counting images size.
This fixes bug #1056981.

This also fixes ImageServe owner which was set wrongly to user rather than
project, and fixes counter type for ImageServe and ImageDownload to delta.

Change-Id: I2512cdfc37f17c425f49a5e5d6d2e25ccb59a3ca
Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
Julien Danjou 2012-10-08 16:26:09 +02:00
parent a725f64f3c
commit 7873fd8bfd
6 changed files with 195 additions and 42 deletions

View File

@ -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,

View File

@ -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,
),
]

View File

@ -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
+++++++++++++++++++

View File

@ -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)

View File

@ -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

View File

@ -3,6 +3,7 @@
# Copyright © 2012 Red Hat Inc.
#
# Author: Eoghan Glynn <eglynn@redhat.com>
# Author: Julien danjou <julien@danjou.info>
#
# 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)