Enable entry points for new declarative meters

Include the new entry point for meters notifications
and deprecate the migrated ones. Also add the exchange
controls to the notification agent directly.

As a first step, this commit converts glance, magnetodb
and orchestration meters.

Partially Implements: blueprint declarative-notifications

Depends-On: I9de94c5ac6349c3b46adbacb77fc877b5201285c

Change-Id: I45ea9ab20bbed1ec12b91a20977e8adb52bf59da
This commit is contained in:
Pradeep Kilambi 2015-07-17 09:33:34 -04:00 committed by Mehdi Abaakouk (sileht)
parent ece5275fce
commit 94b3d40cea
20 changed files with 174 additions and 1152 deletions

View File

@ -93,7 +93,7 @@ class NotificationBase(PluginBase):
super(NotificationBase, self).__init__()
# NOTE(gordc): this is filter rule used by oslo.messaging to dispatch
# messages to an endpoint.
if self.event_types is not None:
if self.event_types:
self.filter_rule = oslo_messaging.NotificationFilter(
event_type='|'.join(self.event_types))
self.manager = manager

View File

@ -0,0 +1,26 @@
#
# 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
EXCHANGE_OPTS = [
cfg.StrOpt('heat_control_exchange',
default='heat',
help="Exchange name for Heat notifications"),
cfg.StrOpt('glance_control_exchange',
default='glance',
help="Exchange name for Glance notifications."),
cfg.StrOpt('magnetodb_control_exchange',
default='magnetodb',
help="Exchange name for Magnetodb notifications."),
]

View File

@ -1,126 +0,0 @@
#
# Copyright 2012 Red Hat, Inc
#
# 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 metering messages from glance notification
events.
"""
from oslo_config import cfg
import oslo_messaging
from ceilometer.agent import plugin_base
from ceilometer import sample
OPTS = [
cfg.StrOpt('glance_control_exchange',
default='glance',
help="Exchange name for Glance notifications."),
]
cfg.CONF.register_opts(OPTS)
class ImageBase(plugin_base.NotificationBase):
"""Base class for image counting."""
@staticmethod
def get_targets(conf):
"""Return a sequence of oslo_messaging.Target
This sequence is defining the exchange and topics to be connected for
this plugin.
"""
return [oslo_messaging.Target(topic=topic,
exchange=conf.glance_control_exchange)
for topic in conf.notification_topics]
class ImageCRUDBase(ImageBase):
event_types = [
'image.update',
'image.upload',
'image.delete',
]
class ImageCRUD(ImageCRUDBase, plugin_base.NonMetricNotificationBase):
def process_notification(self, message):
yield sample.Sample.from_notification(
name=message['event_type'],
type=sample.TYPE_DELTA,
unit='image',
volume=1,
resource_id=message['payload']['id'],
user_id=None,
project_id=message['payload']['owner'],
message=message)
class Image(ImageCRUDBase, plugin_base.NonMetricNotificationBase):
def process_notification(self, message):
yield sample.Sample.from_notification(
name='image',
type=sample.TYPE_GAUGE,
unit='image',
volume=1,
resource_id=message['payload']['id'],
user_id=None,
project_id=message['payload']['owner'],
message=message)
class ImageSize(ImageCRUDBase):
def process_notification(self, message):
yield sample.Sample.from_notification(
name='image.size',
type=sample.TYPE_GAUGE,
unit='B',
volume=message['payload']['size'],
resource_id=message['payload']['id'],
user_id=None,
project_id=message['payload']['owner'],
message=message)
class ImageDownload(ImageBase):
"""Emit image_download sample when an image is downloaded."""
event_types = ['image.send']
def process_notification(self, message):
yield sample.Sample.from_notification(
name='image.download',
type=sample.TYPE_DELTA,
unit='B',
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'],
message=message)
class ImageServe(ImageBase):
"""Emit image_serve sample when an image is served out."""
event_types = ['image.send']
def process_notification(self, message):
yield sample.Sample.from_notification(
name='image.serve',
type=sample.TYPE_DELTA,
unit='B',
volume=message['payload']['bytes_sent'],
resource_id=message['payload']['image_id'],
user_id=None,
project_id=message['payload']['owner_id'],
message=message)

View File

@ -1,79 +0,0 @@
# 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
import oslo_messaging
from ceilometer.agent import plugin_base
from ceilometer import sample
OPTS = [
cfg.StrOpt('magnetodb_control_exchange',
default='magnetodb',
help="Exchange name for Magnetodb notifications."),
]
cfg.CONF.register_opts(OPTS)
class _Base(plugin_base.NotificationBase):
"""Convert magnetodb notification into Samples."""
@staticmethod
def get_targets(conf):
"""Return a sequence of oslo_messaging.Target
Sequence defining the exchange and topics to be connected for this
plugin.
"""
return [oslo_messaging.Target(topic=topic,
exchange=conf.magnetodb_control_exchange)
for topic in conf.notification_topics]
class Table(_Base, plugin_base.NonMetricNotificationBase):
event_types = [
'magnetodb.table.create.end',
'magnetodb.table.delete.end'
]
def process_notification(self, message):
meter_name = '.'.join(message['event_type'].split('.')[:-1])
yield sample.Sample.from_notification(
name=meter_name,
type=sample.TYPE_GAUGE,
unit='table',
volume=1,
resource_id=message['payload']['table_uuid'],
user_id=message['_context_user'],
project_id=message['_context_tenant'],
message=message)
class Index(_Base):
event_types = [
'magnetodb.table.create.end'
]
def process_notification(self, message):
yield sample.Sample.from_notification(
name='magnetodb.table.index.count',
type=sample.TYPE_GAUGE,
unit='index',
volume=message['payload']['index_count'],
resource_id=message['payload']['table_uuid'],
user_id=message['_context_user'],
project_id=message['_context_tenant'],
message=message)

View File

@ -0,0 +1,126 @@
---
metric:
# Image
- name: "image.size"
event_type:
- "image.upload"
- "image.delete"
- "image.update"
type: "gauge"
unit: B
volume: payload.size
resource_id: payload.id
project_id: payload.owner
- name: "image.download"
event_type: "image.send"
type: "delta"
unit: "B"
volume: payload.bytes_sent
resource_id: payload.image_id
user_id: payload.receiver_user_id
project_id: payload.receiver_tenant_id
- name: "image.serve"
event_type: "image.send"
type: "delta"
unit: "B"
volume: payload.bytes_sent
resource_id: payload.image_id
project_id: payload.owner_id
# MagnetoDB
- name: 'magnetodb.table.index.count'
type: 'gauge'
unit: 'index'
event_type: 'magnetodb.table.create.end'
volume: payload.index_count
resource_id: payload.table_uuid
user_id: _context_user
# NOTE: non-metric meters are generally events/existence meters
# These are expected to be DEPRECATED in future releases
#
# Image
- name: "image"
event_type:
- "image.upload"
- "image.delete"
- "image.update"
type: "gauge"
unit: 'image'
volume: 1
resource_id: payload.id
project_id: payload.owner
# Orchestration
- name: 'stack.create'
event_type:
- 'orchestration.stack.create.end'
type: 'delta'
unit: 'stack'
volume: 1
user_id: _context_trustor_user_id
project_id: payload.tenant_id
resource_id: payload.stack_identity
- name: 'stack.update'
event_type:
- 'orchestration.stack.update.end'
type: 'delta'
unit: 'stack'
volume: 1
user_id: _context_trustor_user_id
project_id: payload.tenant_id
resource_id: payload.stack_identity
- name: 'stack.delete'
event_type:
- 'orchestration.stack.delete.end'
type: 'delta'
unit: 'stack'
volume: 1
user_id: _context_trustor_user_id
project_id: payload.tenant_id
resource_id: payload.stack_identity
- name: 'stack.resume'
event_type:
- 'orchestration.stack.resume.end'
type: 'delta'
unit: 'stack'
volume: 1
user_id: _context_trustor_user_id
project_id: payload.tenant_id
resource_id: payload.stack_identity
- name: 'stack.suspend'
event_type:
- 'orchestration.stack.suspend.end'
type: 'delta'
unit: 'stack'
volume: 1
user_id: _context_trustor_user_id
project_id: payload.tenant_id
resource_id: payload.stack_identity
# MagnetoDB
- name: 'magnetodb.table.create'
type: 'gauge'
unit: 'table'
volume: 1
event_type: 'magnetodb.table.create.end'
resource_id: payload.table_uuid
user_id: _context_user
project_id: _context_tenant
- name: 'magnetodb.table.delete'
type: 'gauge'
unit: 'table'
volume: 1
event_type: 'magnetodb.table.delete.end'
resource_id: payload.table_uuid
user_id: _context_user
project_id: _context_tenant

View File

@ -13,6 +13,7 @@
import fnmatch
import os
import pkg_resources
import six
import yaml
@ -95,6 +96,9 @@ def get_config_file():
config_file = cfg.CONF.meter.meter_definitions_cfg_file
if not os.path.exists(config_file):
config_file = cfg.CONF.find_file(config_file)
if not config_file:
config_file = pkg_resources.resource_filename(
__name__, "data/meters.yaml")
return config_file
@ -127,7 +131,7 @@ def setup_meters_config():
else:
LOG.debug(_LE("No Meter Definitions configuration file found!"
" Using default config."))
meters_config = []
meters_config = {}
LOG.info(_LE("Meter Definitions: %s"), meters_config)
@ -135,13 +139,15 @@ def setup_meters_config():
def load_definitions(config_def):
if not config_def:
return []
return [MeterDefinition(event_def)
for event_def in reversed(config_def['metric'])]
class ProcessMeterNotifications(plugin_base.NotificationBase):
event_types = None
event_types = []
def __init__(self, manager):
super(ProcessMeterNotifications, self).__init__(manager)

View File

@ -22,7 +22,7 @@ from ceilometer import sample
cfg.CONF.import_opt('nova_control_exchange',
'ceilometer.compute.notifications')
cfg.CONF.import_opt('glance_control_exchange',
'ceilometer.image.notifications')
'ceilometer.notification')
cfg.CONF.import_opt('neutron_control_exchange',
'ceilometer.network.notifications')
cfg.CONF.import_opt('cinder_control_exchange',

View File

@ -23,6 +23,7 @@ from ceilometer.agent import plugin_base as base
from ceilometer import coordination
from ceilometer.event import endpoint as event_endpoint
from ceilometer.i18n import _, _LI, _LW
from ceilometer import exchange_control
from ceilometer import messaging
from ceilometer import pipeline
from ceilometer import service_base
@ -60,6 +61,7 @@ OPTS = [
"(DEFAULT/transport_url is used if empty)"),
]
cfg.CONF.register_opts(exchange_control.EXCHANGE_OPTS)
cfg.CONF.register_opts(OPTS, group="notification")
cfg.CONF.import_opt('telemetry_driver', 'ceilometer.publisher.messaging',
group='publisher_notifier')

View File

@ -38,7 +38,6 @@ import ceilometer.event.converter
import ceilometer.hardware.discovery
import ceilometer.identity.notifications
import ceilometer.image.glance
import ceilometer.image.notifications
import ceilometer.ipmi.notifications.ironic
import ceilometer.ipmi.platform.intel_node_manager
import ceilometer.ipmi.pollsters
@ -49,7 +48,6 @@ import ceilometer.notification
import ceilometer.nova_client
import ceilometer.objectstore.rgw
import ceilometer.objectstore.swift
import ceilometer.orchestration.notifications
import ceilometer.pipeline
import ceilometer.profiler.notifications
import ceilometer.publisher.messaging
@ -75,13 +73,11 @@ def list_opts():
ceilometer.dispatcher.OPTS,
ceilometer.identity.notifications.OPTS,
ceilometer.image.glance.OPTS,
ceilometer.image.notifications.OPTS,
ceilometer.ipmi.notifications.ironic.OPTS,
ceilometer.middleware.OPTS,
ceilometer.network.notifications.OPTS,
ceilometer.nova_client.OPTS,
ceilometer.objectstore.swift.OPTS,
ceilometer.orchestration.notifications.OPTS,
ceilometer.pipeline.OPTS,
ceilometer.profiler.notifications.OPTS,
ceilometer.sample.OPTS,

View File

@ -1,76 +0,0 @@
# 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 orchestration metering from Heat notification
events.
"""
from oslo_config import cfg
import oslo_messaging
from ceilometer.agent import plugin_base
from ceilometer import sample
OPTS = [
cfg.StrOpt('heat_control_exchange',
default='heat',
help="Exchange name for Heat notifications"),
]
cfg.CONF.register_opts(OPTS)
SERVICE = 'orchestration'
class StackCRUD(plugin_base.NotificationBase,
plugin_base.NonMetricNotificationBase):
resource_name = '%s.stack' % SERVICE
@property
def event_types(self):
return [
'%s.create.end' % self.resource_name,
'%s.update.end' % self.resource_name,
'%s.delete.end' % self.resource_name,
'%s.resume.end' % self.resource_name,
'%s.suspend.end' % self.resource_name,
]
@staticmethod
def get_targets(conf):
"""Return a sequence of oslo_messaging.Target
It is defining the exchange and topics to be connected for this plugin.
"""
return [oslo_messaging.Target(topic=topic,
exchange=conf.heat_control_exchange)
for topic in conf.notification_topics]
def process_notification(self, message):
name = (message['event_type'].replace(self.resource_name, 'stack')
.replace('.end', ''))
project_id = message['payload']['tenant_id']
# Trying to use the trustor_id if trusts is used by Heat,
user_id = (message.get('_context_trustor_user_id') or
message['_context_user_id'])
yield sample.Sample.from_notification(
name=name,
type=sample.TYPE_DELTA,
unit='stack',
volume=1,
resource_id=message['payload']['stack_identity'],
user_id=user_id,
project_id=project_id,
message=message)

View File

@ -33,6 +33,7 @@ from ceilometer.compute.notifications import instance
from ceilometer import messaging
from ceilometer import notification
from ceilometer.publisher import test as test_publisher
from ceilometer import service
from ceilometer.tests import base as tests_base
TEST_NOTICE_CTXT = {
@ -198,6 +199,7 @@ class BaseRealNotification(tests_base.BaseTestCase):
def setUp(self):
super(BaseRealNotification, self).setUp()
self.CONF = self.useFixture(fixture_config.Config()).conf
service.prepare_service([])
self.setup_messaging(self.CONF, 'nova')
pipeline_cfg_file = self.setup_pipeline(['instance', 'memory'])

View File

@ -1,197 +0,0 @@
#
# Copyright 2012 Red Hat Inc.
#
# 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.
import datetime
import mock
from oslotest import base
from ceilometer.image import notifications
from ceilometer import sample
def fake_uuid(x):
return '%s-%s-%s-%s' % (x * 8, x * 4, x * 4, x * 12)
NOW = datetime.datetime.isoformat(datetime.datetime.utcnow())
NOTIFICATION_SEND = {
u'event_type': u'image.send',
u'timestamp': NOW,
u'message_id': fake_uuid('a'),
u'priority': u'INFO',
u'publisher_id': u'images.example.com',
u'payload': {u'receiver_tenant_id': fake_uuid('b'),
u'destination_ip': u'1.2.3.4',
u'bytes_sent': 42,
u'image_id': fake_uuid('c'),
u'receiver_user_id': fake_uuid('d'),
u'owner_id': 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': 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}
NOTIFICATION_UPLOAD = {"message_id": "0c65cb9c-018c-11e2-bc91-5453ed1bbb5f",
"publisher_id": "images.example.com",
"event_type": "image.upload",
"priority": "info",
"payload": IMAGE_META,
"timestamp": NOW}
NOTIFICATION_DELETE = {"message_id": "0c65cb9c-018c-11e2-bc91-5453ed1bbb5f",
"publisher_id": "images.example.com",
"event_type": "image.delete",
"priority": "info",
"payload": IMAGE_META,
"timestamp": NOW}
class TestNotification(base.BaseTestCase):
def _verify_common_counter(self, c, name, volume):
self.assertIsNotNone(c)
self.assertEqual(name, c.name)
self.assertEqual(fake_uuid('c'), c.resource_id)
self.assertEqual(NOW, c.timestamp)
self.assertEqual(volume, c.volume)
metadata = c.resource_metadata
self.assertEqual(u'images.example.com', metadata.get('host'))
def test_image_download(self):
handler = notifications.ImageDownload(mock.Mock())
counters = list(handler.process_notification(NOTIFICATION_SEND))
self.assertEqual(1, len(counters))
download = counters[0]
self._verify_common_counter(download, 'image.download', 42)
self.assertEqual(fake_uuid('d'), download.user_id)
self.assertEqual(fake_uuid('b'), download.project_id)
self.assertEqual(sample.TYPE_DELTA, download.type)
def test_image_serve(self):
handler = notifications.ImageServe(mock.Mock())
counters = list(handler.process_notification(NOTIFICATION_SEND))
self.assertEqual(1, len(counters))
serve = counters[0]
self._verify_common_counter(serve, 'image.serve', 42)
self.assertEqual(fake_uuid('e'), serve.project_id)
self.assertEqual(fake_uuid('d'),
serve.resource_metadata.get('receiver_user_id'))
self.assertEqual(fake_uuid('b'),
serve.resource_metadata.get('receiver_tenant_id'))
self.assertEqual(sample.TYPE_DELTA, serve.type)
def test_image_crud_on_update(self):
handler = notifications.ImageCRUD(mock.Mock())
counters = list(handler.process_notification(NOTIFICATION_UPDATE))
self.assertEqual(1, len(counters))
update = counters[0]
self._verify_common_counter(update, 'image.update', 1)
self.assertEqual(sample.TYPE_DELTA, update.type)
def test_image_on_update(self):
handler = notifications.Image(mock.Mock())
counters = list(handler.process_notification(NOTIFICATION_UPDATE))
self.assertEqual(1, len(counters))
update = counters[0]
self._verify_common_counter(update, 'image', 1)
self.assertEqual(sample.TYPE_GAUGE, update.type)
def test_image_size_on_update(self):
handler = notifications.ImageSize(mock.Mock())
counters = list(handler.process_notification(NOTIFICATION_UPDATE))
self.assertEqual(1, len(counters))
update = counters[0]
self._verify_common_counter(update, 'image.size',
IMAGE_META['size'])
self.assertEqual(sample.TYPE_GAUGE, update.type)
def test_image_crud_on_upload(self):
handler = notifications.ImageCRUD(mock.Mock())
counters = list(handler.process_notification(NOTIFICATION_UPLOAD))
self.assertEqual(1, len(counters))
upload = counters[0]
self._verify_common_counter(upload, 'image.upload', 1)
self.assertEqual(sample.TYPE_DELTA, upload.type)
def test_image_on_upload(self):
handler = notifications.Image(mock.Mock())
counters = list(handler.process_notification(NOTIFICATION_UPLOAD))
self.assertEqual(1, len(counters))
upload = counters[0]
self._verify_common_counter(upload, 'image', 1)
self.assertEqual(sample.TYPE_GAUGE, upload.type)
def test_image_size_on_upload(self):
handler = notifications.ImageSize(mock.Mock())
counters = list(handler.process_notification(NOTIFICATION_UPLOAD))
self.assertEqual(1, len(counters))
upload = counters[0]
self._verify_common_counter(upload, 'image.size',
IMAGE_META['size'])
self.assertEqual(sample.TYPE_GAUGE, upload.type)
def test_image_crud_on_delete(self):
handler = notifications.ImageCRUD(mock.Mock())
counters = list(handler.process_notification(NOTIFICATION_DELETE))
self.assertEqual(1, len(counters))
delete = counters[0]
self._verify_common_counter(delete, 'image.delete', 1)
self.assertEqual(sample.TYPE_DELTA, delete.type)
def test_image_on_delete(self):
handler = notifications.Image(mock.Mock())
counters = list(handler.process_notification(NOTIFICATION_DELETE))
self.assertEqual(1, len(counters))
delete = counters[0]
self._verify_common_counter(delete, 'image', 1)
self.assertEqual(sample.TYPE_GAUGE, delete.type)
def test_image_size_on_delete(self):
handler = notifications.ImageSize(mock.Mock())
counters = list(handler.process_notification(NOTIFICATION_DELETE))
self.assertEqual(1, len(counters))
delete = counters[0]
self._verify_common_counter(delete, 'image.size',
IMAGE_META['size'])
self.assertEqual(sample.TYPE_GAUGE, delete.type)

View File

@ -1,124 +0,0 @@
# 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.
import datetime
import mock
from oslo_config import fixture as fixture_config
from ceilometer.meter import notifications
from ceilometer import sample
from ceilometer.tests import base as test
def fake_uuid(x):
return '%s-%s-%s-%s' % (x * 8, x * 4, x * 4, x * 12)
NOW = datetime.datetime.isoformat(datetime.datetime.utcnow())
TABLE_CREATE_PAYLOAD = {
u'table_uuid': fake_uuid('r'),
u'index_count': 2,
u'table_name': u'email_data'
}
TABLE_DELETE_PAYLOAD = {
u'table_uuid': fake_uuid('r'),
u'table_name': u'email_data'
}
NOTIFICATION_TABLE_CREATE = {
u'_context_request_id': u'req-d6e9b7ec-976f-443f-ba6e-e2b89b18aa75',
u'_context_tenant': fake_uuid('t'),
u'_context_user': fake_uuid('u'),
u'_context_auth_token': u'',
u'_context_show_deleted': False,
u'_context_is_admin': u'False',
u'_context_read_only': False,
'payload': TABLE_CREATE_PAYLOAD,
'publisher_id': u'magnetodb.winterfell.com',
'message_id': u'3d71fb8a-f1d7-4a4e-b29f-7a711a761ba1',
'event_type': u'magnetodb.table.create.end',
'timestamp': NOW,
'priority': 'info'
}
NOTIFICATION_TABLE_DELETE = {
u'_context_request_id': u'req-d6e9b7ec-976f-443f-ba6e-e2b89b18aa75',
u'_context_tenant': fake_uuid('t'),
u'_context_user': fake_uuid('u'),
u'_context_auth_token': u'',
u'_context_show_deleted': False,
u'_context_is_admin': u'False',
u'_context_read_only': False,
'payload': TABLE_DELETE_PAYLOAD,
'publisher_id': u'magnetodb.winterfell.com',
'message_id': u'4c8f5940-3c90-41af-ac16-f0e3055a305d',
'event_type': u'magnetodb.table.delete.end',
'timestamp': NOW,
'priority': 'info'
}
class TestNotification(test.BaseTestCase):
def setUp(self):
super(TestNotification, self).setUp()
self.CONF = self.useFixture(fixture_config.Config()).conf
self.CONF.set_override(
'meter_definitions_cfg_file',
self.path_get('etc/ceilometer/meters.yaml'), group='meter')
self.handler = notifications.ProcessMeterNotifications(mock.Mock())
def _verify_common_counter(self, c, name, volume):
self.assertIsNotNone(c)
self.assertEqual(name, c.name)
self.assertEqual(fake_uuid('r'), c.resource_id)
self.assertEqual(NOW, c.timestamp)
self.assertEqual(volume, c.volume)
metadata = c.resource_metadata
self.assertEqual(u'magnetodb.winterfell.com', metadata.get('host'))
def test_create_table(self):
counters = list(self.handler.process_notification(
NOTIFICATION_TABLE_CREATE))
self.assertEqual(2, len(counters))
table = [item for item in counters
if item.name == "magnetodb.table.create"][0]
self._verify_common_counter(table, 'magnetodb.table.create', 1)
self.assertEqual(fake_uuid('u'), table.user_id)
self.assertEqual(fake_uuid('t'), table.project_id)
self.assertEqual(sample.TYPE_GAUGE, table.type)
def test_delete_table(self):
counters = list(self.handler.process_notification(
NOTIFICATION_TABLE_DELETE))
self.assertEqual(1, len(counters))
table = counters[0]
self._verify_common_counter(table, 'magnetodb.table.delete', 1)
self.assertEqual(fake_uuid('u'), table.user_id)
self.assertEqual(fake_uuid('t'), table.project_id)
self.assertEqual(sample.TYPE_GAUGE, table.type)
def test_index_count(self):
counters = list(self.handler.process_notification(
NOTIFICATION_TABLE_CREATE))
self.assertEqual(2, len(counters))
table = [item for item in counters
if item.name == "magnetodb.table.index.count"][0]
self._verify_common_counter(table, 'magnetodb.table.index.count', 2)
self.assertEqual(fake_uuid('u'), table.user_id)
self.assertEqual(fake_uuid('t'), table.project_id)
self.assertEqual(sample.TYPE_GAUGE, table.type)

View File

@ -20,6 +20,7 @@ from oslo_config import fixture as fixture_config
from oslo_utils import fileutils
from ceilometer.meter import notifications
from ceilometer import service as ceilometer_service
from ceilometer.tests import base as test
NOTIFICATION = {
@ -82,11 +83,13 @@ class TestMeterProcessing(test.BaseTestCase):
def setUp(self):
super(TestMeterProcessing, self).setUp()
self.CONF = self.useFixture(fixture_config.Config()).conf
self.CONF.set_override(
'meter_definitions_cfg_file',
self.path_get('etc/ceilometer/meters.yaml'), group='meter')
ceilometer_service.prepare_service([])
self.handler = notifications.ProcessMeterNotifications(mock.Mock())
def test_fallback_meter_path(self):
fall_bak_path = notifications.get_config_file()
self.assertIn("meter/data/meters.yaml", fall_bak_path)
def __setup_meter_def_file(self, cfg):
if six.PY3:
cfg = cfg.encode('utf-8')

View File

@ -1,138 +0,0 @@
# 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.
import datetime
import mock
from oslo_config import cfg
from oslo_config import fixture as fixture_config
from oslo_log import log
from ceilometer.meter import notifications
from ceilometer import sample
from ceilometer.tests import base as test
NOW = datetime.datetime.isoformat(datetime.datetime.utcnow())
TENANT_ID = u'4c35985848bf4419b3f3d52c22e5792d'
STACK_NAME = u'AS1-ASGroup-53sqbo7sor7i'
STACK_ID = u'cb4a6fd1-1f5d-4002-ae91-9b91573cfb03'
USER_NAME = u'demo'
USER_ID = u'2e61f25ec63a4f6c954a6245421448a4'
TRUSTOR_ID = u'foo-Trustor-Id'
STACK_ARN = u'arn:openstack:heat::%s:stacks/%s/%s' % (TENANT_ID,
STACK_NAME,
STACK_ID)
CONF = cfg.CONF
log.register_options(CONF)
CONF.set_override('use_stderr', True)
LOG = log.getLogger(__name__)
def stack_notification_for(operation, use_trust=None):
if use_trust:
trust_id = 'footrust'
trustor_id = TRUSTOR_ID
else:
trust_id = None
trustor_id = None
return {
u'event_type': 'orchestration.stack.%s.end' % operation,
u'_context_roles': [
u'Member',
],
u'_context_request_id': u'req-cf24cf30-af35-4a47-ae29-e74d75ebc6de',
u'_context_auth_url': u'http://0.1.0.1:1010/v2.0',
u'timestamp': NOW,
u'_unique_id': u'1afb4283660f410c802af4d5992a39f2',
u'_context_tenant_id': TENANT_ID,
u'payload': {
u'state_reason': u'Stack create completed successfully',
u'user_id': USER_NAME,
u'stack_identity': STACK_ARN,
u'stack_name': STACK_NAME,
u'tenant_id': TENANT_ID,
u'create_at': u'2014-01-27T13:13:19Z',
u'state': u'CREATE_COMPLETE'
},
u'_context_username': USER_NAME,
u'_context_auth_token': u'MIISAwYJKoZIhvcNAQcCoII...',
u'_context_password': u'password',
u'_context_user_id': USER_ID,
u'_context_trustor_user_id': trustor_id,
u'_context_aws_creds': None,
u'_context_show_deleted': False,
u'_context_tenant': USER_NAME,
u'_context_trust_id': trust_id,
u'priority': u'INFO',
u'_context_is_admin': False,
u'_context_user': USER_NAME,
u'publisher_id': u'orchestration.node-n5x66lxdy67d',
u'message_id': u'ef921faa-7f7b-4854-8b86-a424ab93c96e',
}
class TestNotification(test.BaseTestCase):
def setUp(self):
super(TestNotification, self).setUp()
self.CONF = self.useFixture(fixture_config.Config()).conf
self.CONF.set_override(
'meter_definitions_cfg_file',
self.path_get('etc/ceilometer/meters.yaml'), group='meter')
self.handler = notifications.ProcessMeterNotifications(mock.Mock())
def _verify_common_sample(self, s, name, volume):
self.assertIsNotNone(s)
self.assertEqual('stack.%s' % name, s.name)
self.assertEqual(NOW, s.timestamp)
self.assertEqual(sample.TYPE_DELTA, s.type)
self.assertEqual(TENANT_ID, s.project_id)
self.assertEqual(STACK_ARN, s.resource_id)
metadata = s.resource_metadata
self.assertEqual(u'orchestration.node-n5x66lxdy67d',
metadata.get('host'))
def _test_operation(self, operation, trust=None):
notif = stack_notification_for(operation, trust)
data = list(self.handler.process_notification(notif))
self.assertEqual(1, len(data))
if trust:
self.assertEqual(TRUSTOR_ID, data[0].user_id)
else:
self.assertEqual(USER_ID, data[0].user_id)
self._verify_common_sample(data[0], operation, 1)
def test_create(self):
self._test_operation('create')
def test_create_trust(self):
self._test_operation('create', trust=True)
def test_update(self):
self._test_operation('update')
def test_delete(self):
self._test_operation('delete')
def test_resume(self):
self._test_operation('resume')
def test_suspend(self):
self._test_operation('suspend')

View File

@ -256,6 +256,7 @@ function configure_ceilometer {
cp $CEILOMETER_DIR/etc/ceilometer/event_definitions.yaml $CEILOMETER_CONF_DIR
cp $CEILOMETER_DIR/etc/ceilometer/gnocchi_archive_policy_map.yaml $CEILOMETER_CONF_DIR
cp $CEILOMETER_DIR/etc/ceilometer/gnocchi_resources.yaml $CEILOMETER_CONF_DIR
cp $CEILOMETER_DIR/ceilometer/meter/data/meters.yaml $CEILOMETER_CONF_DIR
if [ "$CEILOMETER_PIPELINE_INTERVAL" ]; then
sed -i "s/interval:.*/interval: ${CEILOMETER_PIPELINE_INTERVAL}/" $CEILOMETER_CONF_DIR/pipeline.yaml

View File

@ -1,393 +0,0 @@
---
metric:
- name: "image.size"
event_type:
- "image.upload"
- "image.delete"
- "image.update"
type: "gauge"
unit: B
volume: payload.size
resource_id: payload.id
project_id: payload.owner
- name: "image.download"
event_type: "image.send"
type: "delta"
unit: "B"
volume: payload.bytes_sent
resource_id: payload.image_id
user_id: payload.receiver_user_id
project_id: payload.receiver_tenant_id
- name: "image.serve"
event_type: "image.send"
type: "delta"
unit: "B"
volume: payload.bytes_sent
resource_id: payload.image_id
project_id: payload.owner_id
- name: 'bandwidth'
event_type: 'l3.meter'
type: 'delta'
unit: 'B'
volume: payload.bytes
project_id: payload.tenant_id
resource_id: payload.label_id
- name: 'magnetodb.table.index.count'
type: 'gauge'
unit: 'index'
event_type: 'magnetodb.table.create.end'
volume: payload.index_count
resource_id: payload.table_uuid
user_id: _context_user
- name: 'memory'
event_type: 'compute.instance.*'
type: 'gauge'
unit: 'MB'
volume: payload.memory_mb
user_id: payload.user_id
project_id: payload.tenant_id
resource_id: payload.instance_id
- name: 'vcpus'
event_type: 'compute.instance.*'
type: 'gauge'
unit: 'vcpu'
volume: payload.vcpus
user_id: payload.user_id
project_id: payload.tenant_id
resource_id: payload.instance_id
- name: 'disk.root.size'
event_type: 'compute.instance.*'
type: 'gauge'
unit: 'GB'
volume: payload.root_gb
user_id: payload.user_id
project_id: payload.tenant_id
resource_id: payload.instance_id
- name: 'disk.ephemeral.size'
event_type: 'compute.instance.*'
type: 'gauge'
unit: 'GB'
volume: payload.ephemeral_gb
user_id: payload.user_id
project_id: payload.tenant_id
resource_id: payload.instance_id
- name: 'volume.size'
event_type:
- 'volume.exists'
- 'volume.create.*'
- 'volume.delete.*'
- 'volume.resize.*'
- 'volume.attach.*'
- 'volume.detach.*'
- 'volume.update.*'
type: 'gauge'
unit: 'GB'
volume: payload.size
user_id: payload.user_id
project_id: payload.tenant_id
resource_id: payload.volume_id
- name: 'snapshot.size'
event_type:
- 'snapshot.exists'
- 'snapshot.create.*'
- 'snapshot.delete.*'
type: 'gauge'
unit: 'GB'
volume: payload.volume_size
user_id: payload.user_id
project_id: payload.tenant_id
resource_id: payload.snapshot_id
# NOTE: non-metric meters are generally events/existence meters
# These are expected to be DEPRECATED in future releases
#
- name: 'stack.create'
event_type:
- 'orchestration.stack.create.end'
type: 'delta'
unit: 'stack'
volume: 1
user_id: _context_trustor_user_id
project_id: payload.tenant_id
resource_id: payload.stack_identity
- name: 'stack.update'
event_type:
- 'orchestration.stack.update.end'
type: 'delta'
unit: 'stack'
volume: 1
user_id: _context_trustor_user_id
project_id: payload.tenant_id
resource_id: payload.stack_identity
- name: 'stack.delete'
event_type:
- 'orchestration.stack.delete.end'
type: 'delta'
unit: 'stack'
volume: 1
user_id: _context_trustor_user_id
project_id: payload.tenant_id
resource_id: payload.stack_identity
- name: 'stack.resume'
event_type:
- 'orchestration.stack.resume.end'
type: 'delta'
unit: 'stack'
volume: 1
user_id: _context_trustor_user_id
project_id: payload.tenant_id
resource_id: payload.stack_identity
- name: 'stack.suspend'
event_type:
- 'orchestration.stack.suspend.end'
type: 'delta'
unit: 'stack'
volume: 1
user_id: _context_trustor_user_id
project_id: payload.tenant_id
resource_id: payload.stack_identity
- name: 'magnetodb.table.create'
type: 'gauge'
unit: 'table'
volume: 1
event_type: 'magnetodb.table.create.end'
resource_id: payload.table_uuid
user_id: _context_user
project_id: _context_tenant
- name: 'magnetodb.table.delete'
type: 'gauge'
unit: 'table'
volume: 1
event_type: 'magnetodb.table.delete.end'
resource_id: payload.table_uuid
user_id: _context_user
project_id: _context_tenant
- name: 'volume'
type: 'gauge'
unit: 'volume'
volume: 1
event_type:
- 'volume.exists'
- 'volume.create.*'
- 'volume.delete.*'
- 'volume.resize.*'
- 'volume.attach.*'
- 'volume.detach.*'
- 'volume.update.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.exists'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.exists'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.create.start'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.create.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.create.end'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.create.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.delete.start'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.delete.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.delete.end'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.delete.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.update.end'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.update.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.update.start'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.update.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.resize.end'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.resize.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.resize.start'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.resize.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.attach.end'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.attach.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.attach.start'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.attach.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.detach.end'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.detach.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'volume.detach.start'
type: 'delta'
unit: 'volume'
volume: 1
event_type:
- 'volume.detach.*'
resource_id: payload.volume_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'snapshot'
type: 'gauge'
unit: 'snapshot'
volume: 1
event_type:
- 'snapshot.exists'
- 'snapshot.create.*'
- 'snapshot.delete.*'
resource_id: payload.snapshot_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'snapshot.exists'
type: 'delta'
unit: 'snapshot'
volume: 1
event_type:
- 'snapshot.exists'
resource_id: payload.snapshot_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'snapshot.create.start'
type: 'delta'
unit: 'snapshot'
volume: 1
event_type:
- 'snapshot.create.*'
resource_id: payload.snapshot_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'snapshot.create.end'
type: 'delta'
unit: 'snapshot'
volume: 1
event_type:
- 'snapshot.create.*'
resource_id: payload.snapshot_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'snapshot.delete.start'
type: 'delta'
unit: 'snapshot'
volume: 1
event_type:
- 'snapshot.delete.*'
resource_id: payload.snapshot_id
user_id: payload.user_id
project_id: payload.tenant_id
- name: 'snapshot.delete.end'
type: 'delta'
unit: 'snapshot'
volume: 1
event_type:
- 'snapshot.delete.*'
resource_id: payload.snapshot_id
user_id: payload.user_id
project_id: payload.tenant_id

View File

@ -28,8 +28,6 @@ packages =
[entry_points]
ceilometer.notification =
magnetodb_table = ceilometer.key_value_storage.notifications:Table
magnetodb_index_count = ceilometer.key_value_storage.notifications:Index
instance = ceilometer.compute.notifications.instance:Instance
instance_flavor = ceilometer.compute.notifications.instance:InstanceFlavor
instance_delete = ceilometer.compute.notifications.instance:InstanceDelete
@ -61,11 +59,6 @@ ceilometer.notification =
project = ceilometer.identity.notifications:Project
trust = ceilometer.identity.notifications:Trust
role_assignment = ceilometer.identity.notifications:RoleAssignment
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
subnet = ceilometer.network.notifications:Subnet
port = ceilometer.network.notifications:Port
@ -74,7 +67,6 @@ ceilometer.notification =
bandwidth = ceilometer.network.notifications:Bandwidth
http.request = ceilometer.middleware:HTTPRequest
http.response = ceilometer.middleware:HTTPResponse
stack_crud = ceilometer.orchestration.notifications:StackCRUD
data_processing = ceilometer.data_processing.notifications:DataProcessing
profiler = ceilometer.profiler.notifications:ProfilerNotifications
hardware.ipmi.temperature = ceilometer.ipmi.notifications.ironic:TemperatureSensorNotification
@ -97,6 +89,7 @@ ceilometer.notification =
_sample = ceilometer.telemetry.notifications:TelemetryApiPost
trove.instance.exists = ceilometer.database.notifications:InstanceExists
dns.domain.exists = ceilometer.dns.notifications:DomainExists
meter = ceilometer.meter.notifications:ProcessMeterNotifications
ceilometer.discover =
local_instances = ceilometer.compute.discovery:InstanceDiscovery