enable event pipeline

this patch enables the event pipeline. by default the event pipeline
will publish events straight to db.

Implements: blueprint notification-pipelines
Change-Id: I7cad6dd39d50a42f2af347f79a24fc776d261896
This commit is contained in:
gordon chung
2015-01-08 12:55:32 -05:00
parent 9a52e4081d
commit c834442ff2
10 changed files with 175 additions and 91 deletions

View File

@@ -17,26 +17,29 @@ import logging
import oslo.messaging import oslo.messaging
from oslo_config import cfg from oslo_config import cfg
from oslo_context import context
from stevedore import extension from stevedore import extension
import ceilometer
from ceilometer import dispatcher
from ceilometer.event import converter as event_converter from ceilometer.event import converter as event_converter
from ceilometer.event.storage import models
from ceilometer.i18n import _ from ceilometer.i18n import _
from ceilometer import messaging from ceilometer import messaging
from ceilometer.publisher import utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class EventsNotificationEndpoint(object): class EventsNotificationEndpoint(object):
def __init__(self): def __init__(self, transporter):
super(EventsNotificationEndpoint, self).__init__() super(EventsNotificationEndpoint, self).__init__()
self.dispatcher_manager = dispatcher.load_dispatcher_manager()
LOG.debug(_('Loading event definitions')) LOG.debug(_('Loading event definitions'))
self.ctxt = context.get_admin_context()
self.event_converter = event_converter.setup_events( self.event_converter = event_converter.setup_events(
extension.ExtensionManager( extension.ExtensionManager(
namespace='ceilometer.event.trait_plugin')) namespace='ceilometer.event.trait_plugin'))
self.transporter = transporter
# NOTE(gordc): if no publisher, this isn't a PipelineManager and
# data should be requeued.
self.requeue = not hasattr(transporter, 'publisher')
def info(self, ctxt, publisher_id, event_type, payload, metadata): def info(self, ctxt, publisher_id, event_type, payload, metadata):
"""Convert message to Ceilometer Event. """Convert message to Ceilometer Event.
@@ -56,19 +59,21 @@ class EventsNotificationEndpoint(object):
self.process_notification(notification) self.process_notification(notification)
def process_notification(self, notification): def process_notification(self, notification):
event = self.event_converter.to_event(notification)
if event is not None:
LOG.debug(_('Saving event "%s"'), event.event_type)
problem_events = []
for dispatcher_ext in self.dispatcher_manager:
try: try:
problem_events.extend( event = self.event_converter.to_event(notification)
dispatcher_ext.obj.record_events(event)) if event is not None:
except ceilometer.NotImplementedError: if self.requeue:
LOG.warn(_('Event is not implemented with the storage' for notifier in self.transporter:
' backend')) notifier.sample(
if models.Event.UNKNOWN_PROBLEM in [x[0] for x in problem_events]: self.ctxt.to_dict(),
event_type='pipeline.event',
payload=[utils.message_from_event(
event, cfg.CONF.publisher.metering_secret)])
else:
with self.transporter.publisher(self.ctxt) as p:
p(event)
except Exception:
if not cfg.CONF.notification.ack_on_event_error: if not cfg.CONF.notification.ack_on_event_error:
return oslo.messaging.NotificationResult.REQUEUE return oslo.messaging.NotificationResult.REQUEUE
raise
return oslo.messaging.NotificationResult.HANDLED return oslo.messaging.NotificationResult.HANDLED

View File

@@ -17,6 +17,11 @@ from oslo_utils import timeutils
from ceilometer.storage import base from ceilometer.storage import base
def serialize_dt(value):
"""Serializes parameter if it is datetime."""
return value.isoformat() if hasattr(value, 'isoformat') else value
class Event(base.Model): class Event(base.Model):
"""A raw event from the source system. Events have Traits. """A raw event from the source system. Events have Traits.
@@ -51,6 +56,12 @@ class Event(base.Model):
(self.message_id, self.event_type, self.generated, (self.message_id, self.event_type, self.generated,
" ".join(trait_list))) " ".join(trait_list)))
def serialize(self):
return {'message_id': self.message_id,
'event_type': self.event_type,
'generated': serialize_dt(self.generated),
'traits': [trait.serialize() for trait in self.traits]}
class Trait(base.Model): class Trait(base.Model):
"""A Trait is a key/value pair of data on an Event. """A Trait is a key/value pair of data on an Event.
@@ -80,6 +91,9 @@ class Trait(base.Model):
def __repr__(self): def __repr__(self):
return "<Trait: %s %d %s>" % (self.name, self.dtype, self.value) return "<Trait: %s %d %s>" % (self.name, self.dtype, self.value)
def serialize(self):
return self.name, self.dtype, serialize_dt(self.value)
def get_type_name(self): def get_type_name(self):
return self.get_name_by_type(self.dtype) return self.get_name_by_type(self.dtype)

View File

@@ -77,22 +77,33 @@ class NotificationService(os_service.Service):
invoke_args=(transporter, ) invoke_args=(transporter, )
) )
def _get_notifier(self, transport, pipe):
return oslo.messaging.Notifier(
transport,
driver=cfg.CONF.publisher_notifier.metering_driver,
publisher_id='ceilometer.notification',
topic='%s-%s' % (self.NOTIFICATION_IPC, pipe.name))
def start(self): def start(self):
super(NotificationService, self).start() super(NotificationService, self).start()
self.pipeline_manager = pipeline.setup_pipeline() self.pipeline_manager = pipeline.setup_pipeline()
if cfg.CONF.notification.store_events:
self.event_pipeline_manager = pipeline.setup_event_pipeline()
transport = messaging.get_transport() transport = messaging.get_transport()
self.partition_coordinator = coordination.PartitionCoordinator() self.partition_coordinator = coordination.PartitionCoordinator()
self.partition_coordinator.start() self.partition_coordinator.start()
event_transporter = None
if cfg.CONF.notification.workload_partitioning: if cfg.CONF.notification.workload_partitioning:
transporter = [] transporter = []
for pipe in self.pipeline_manager.pipelines: for pipe in self.pipeline_manager.pipelines:
transporter.append(oslo.messaging.Notifier( transporter.append(self._get_notifier(transport, pipe))
transport, if cfg.CONF.notification.store_events:
driver=cfg.CONF.publisher_notifier.metering_driver, event_transporter = []
publisher_id='ceilometer.notification', for pipe in self.event_pipeline_manager.pipelines:
topic='%s-%s' % (self.NOTIFICATION_IPC, pipe.name))) event_transporter.append(self._get_notifier(transport,
pipe))
self.ctxt = context.get_admin_context() self.ctxt = context.get_admin_context()
self.group_id = self.NOTIFICATION_NAMESPACE self.group_id = self.NOTIFICATION_NAMESPACE
@@ -105,10 +116,12 @@ class NotificationService(os_service.Service):
# beeen registered by oslo.messaging # beeen registered by oslo.messaging
messaging.get_notifier(transport, '') messaging.get_notifier(transport, '')
transporter = self.pipeline_manager transporter = self.pipeline_manager
if cfg.CONF.notification.store_events:
event_transporter = self.event_pipeline_manager
self.group_id = None self.group_id = None
self.listeners = self.pipeline_listeners = [] self.listeners = self.pipeline_listeners = []
self._configure_main_queue_listeners(transporter) self._configure_main_queue_listeners(transporter, event_transporter)
if cfg.CONF.notification.workload_partitioning: if cfg.CONF.notification.workload_partitioning:
self.partition_coordinator.join_group(self.group_id) self.partition_coordinator.join_group(self.group_id)
@@ -124,10 +137,9 @@ class NotificationService(os_service.Service):
# Add a dummy thread to have wait() working # Add a dummy thread to have wait() working
self.tg.add_timer(604800, lambda: None) self.tg.add_timer(604800, lambda: None)
def _configure_main_queue_listeners(self, transporter): def _configure_main_queue_listeners(self, transporter, event_transporter):
self.notification_manager = self._get_notifications_manager( notification_manager = self._get_notifications_manager(transporter)
transporter) if not list(notification_manager):
if not list(self.notification_manager):
LOG.warning(_('Failed to load any notification handlers for %s'), LOG.warning(_('Failed to load any notification handlers for %s'),
self.NOTIFICATION_NAMESPACE) self.NOTIFICATION_NAMESPACE)
@@ -135,10 +147,11 @@ class NotificationService(os_service.Service):
endpoints = [] endpoints = []
if cfg.CONF.notification.store_events: if cfg.CONF.notification.store_events:
endpoints = [event_endpoint.EventsNotificationEndpoint()] endpoints.append(
event_endpoint.EventsNotificationEndpoint(event_transporter))
targets = [] targets = []
for ext in self.notification_manager: for ext in notification_manager:
handler = ext.obj handler = ext.obj
LOG.debug(_('Event types from %(name)s: %(type)s' LOG.debug(_('Event types from %(name)s: %(type)s'
' (ack_on_error=%(error)s)') % ' (ack_on_error=%(error)s)') %
@@ -176,16 +189,22 @@ class NotificationService(os_service.Service):
def _configure_pipeline_listeners(self): def _configure_pipeline_listeners(self):
self.pipeline_listeners = [] self.pipeline_listeners = []
ev_pipes = []
if cfg.CONF.notification.store_events:
ev_pipes = self.event_pipeline_manager.pipelines
partitioned = self.partition_coordinator.extract_my_subset( partitioned = self.partition_coordinator.extract_my_subset(
self.group_id, self.pipeline_manager.pipelines) self.group_id, self.pipeline_manager.pipelines + ev_pipes)
transport = messaging.get_transport() transport = messaging.get_transport()
for pipe in partitioned: for pipe in partitioned:
LOG.debug(_('Pipeline endpoint: %s'), pipe.name) LOG.debug(_('Pipeline endpoint: %s'), pipe.name)
pipe_endpoint = (pipeline.EventPipelineEndpoint
if isinstance(pipe, pipeline.EventPipeline) else
pipeline.SamplePipelineEndpoint)
listener = messaging.get_notification_listener( listener = messaging.get_notification_listener(
transport, transport,
[oslo.messaging.Target( [oslo.messaging.Target(
topic='%s-%s' % (self.NOTIFICATION_IPC, pipe.name))], topic='%s-%s' % (self.NOTIFICATION_IPC, pipe.name))],
[pipeline.PipelineEndpoint(self.ctxt, pipe)]) [pipe_endpoint(self.ctxt, pipe)])
listener.start() listener.start()
self.pipeline_listeners.append(listener) self.pipeline_listeners.append(listener)

View File

@@ -22,9 +22,11 @@ import fnmatch
import os import os
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import timeutils
import six import six
import yaml import yaml
from ceilometer.event.storage import models
from ceilometer.i18n import _ from ceilometer.i18n import _
from ceilometer.openstack.common import log from ceilometer.openstack.common import log
from ceilometer import publisher from ceilometer import publisher
@@ -57,13 +59,19 @@ class PipelineException(Exception):
return 'Pipeline %s: %s' % (self.pipeline_cfg, self.msg) return 'Pipeline %s: %s' % (self.pipeline_cfg, self.msg)
@six.add_metaclass(abc.ABCMeta)
class PipelineEndpoint(object): class PipelineEndpoint(object):
def __init__(self, context, pipeline): def __init__(self, context, pipeline):
self.publish_context = PublishContext(context, [pipeline]) self.publish_context = PublishContext(context, [pipeline])
@abc.abstractmethod
def sample(self, ctxt, publisher_id, event_type, payload, metadata):
pass
class SamplePipelineEndpoint(PipelineEndpoint):
def sample(self, ctxt, publisher_id, event_type, payload, metadata): def sample(self, ctxt, publisher_id, event_type, payload, metadata):
"""RPC endpoint for pipeline messages."""
samples = [ samples = [
sample_util.Sample(name=s['counter_name'], sample_util.Sample(name=s['counter_name'],
type=s['counter_type'], type=s['counter_type'],
@@ -81,6 +89,23 @@ class PipelineEndpoint(object):
p(samples) p(samples)
class EventPipelineEndpoint(PipelineEndpoint):
def sample(self, ctxt, publisher_id, event_type, payload, metadata):
events = [
models.Event(
message_id=ev['message_id'],
event_type=ev['event_type'],
generated=timeutils.normalize_time(
timeutils.parse_isotime(ev['generated'])),
traits=[models.Trait(name, dtype,
models.Trait.convert_value(dtype, value))
for name, dtype, value in ev['traits']])
for ev in payload
]
with self.publish_context as p:
p(events)
class PublishContext(object): class PublishContext(object):
def __init__(self, context, pipelines=None): def __init__(self, context, pipelines=None):
@@ -280,7 +305,8 @@ class Sink(object):
# Support old format without URL # Support old format without URL
p = p + "://" p = p + "://"
try: try:
self.publishers.append(publisher.get_publisher(p)) self.publishers.append(publisher.get_publisher(p,
self.NAMESPACE))
except Exception: except Exception:
LOG.exception(_("Unable to load publisher %s"), p) LOG.exception(_("Unable to load publisher %s"), p)
@@ -311,6 +337,8 @@ class Sink(object):
class EventSink(Sink): class EventSink(Sink):
NAMESPACE = 'ceilometer.event.publisher'
def publish_events(self, ctxt, events): def publish_events(self, ctxt, events):
if events: if events:
for p in self.publishers: for p in self.publishers:
@@ -329,6 +357,8 @@ class EventSink(Sink):
class SampleSink(Sink): class SampleSink(Sink):
NAMESPACE = 'ceilometer.publisher'
def _transform_sample(self, start, ctxt, sample): def _transform_sample(self, start, ctxt, sample):
try: try:
for transformer in self.transformers[start:]: for transformer in self.transformers[start:]:

View File

@@ -13,11 +13,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo.config import cfg from oslo.config import cfg
from oslo.utils import timeutils from oslo.utils import timeutils
import ceilometer
from ceilometer.dispatcher import database from ceilometer.dispatcher import database
from ceilometer import publisher from ceilometer import publisher
from ceilometer.publisher import utils from ceilometer.publisher import utils
@@ -33,7 +31,9 @@ class DirectPublisher(publisher.PublisherBase):
def __init__(self, parsed_url): def __init__(self, parsed_url):
super(DirectPublisher, self).__init__(parsed_url) super(DirectPublisher, self).__init__(parsed_url)
self.meter_conn = database.DatabaseDispatcher(cfg.CONF).meter_conn dispatcher = database.DatabaseDispatcher(cfg.CONF)
self.meter_conn = dispatcher.meter_conn
self.event_conn = dispatcher.event_conn
def publish_samples(self, context, samples): def publish_samples(self, context, samples):
if not isinstance(samples, list): if not isinstance(samples, list):
@@ -54,4 +54,7 @@ class DirectPublisher(publisher.PublisherBase):
self.meter_conn.record_metering_data(meter) self.meter_conn.record_metering_data(meter)
def publish_events(self, context, events): def publish_events(self, context, events):
raise ceilometer.NotImplementedError if not isinstance(events, list):
events = [events]
self.event_conn.record_events(events)

View File

@@ -118,3 +118,13 @@ def meter_message_from_counter(sample, secret):
} }
msg['message_signature'] = compute_signature(msg, secret) msg['message_signature'] = compute_signature(msg, secret)
return msg return msg
def message_from_event(event, secret):
"""Make an event message ready to be published or stored.
Returns a serialized model of Event containing an event message
"""
msg = event.serialize()
msg['message_signature'] = compute_signature(msg, secret)
return msg

View File

@@ -18,11 +18,8 @@ import mock
import oslo.messaging import oslo.messaging
from oslo_config import cfg from oslo_config import cfg
from oslo_config import fixture as fixture_config from oslo_config import fixture as fixture_config
from stevedore import extension
import ceilometer
from ceilometer.event import endpoint as event_endpoint from ceilometer.event import endpoint as event_endpoint
from ceilometer.event.storage import models
from ceilometer.tests import base as tests_base from ceilometer.tests import base as tests_base
TEST_NOTICE_CTXT = { TEST_NOTICE_CTXT = {
@@ -91,46 +88,21 @@ class TestEventEndpoint(tests_base.BaseTestCase):
self.CONF.set_override("store_events", True, group="notification") self.CONF.set_override("store_events", True, group="notification")
self.setup_messaging(self.CONF) self.setup_messaging(self.CONF)
self.mock_dispatcher = mock.MagicMock() self.mock_pm = mock.MagicMock()
self.endpoint = event_endpoint.EventsNotificationEndpoint() self.endpoint = event_endpoint.EventsNotificationEndpoint(self.mock_pm)
(self.endpoint.
dispatcher_manager) = (extension.ExtensionManager.
make_test_instance([extension.
Extension('test', None,
None,
self.
mock_dispatcher)
]))
self.endpoint.event_converter = mock.MagicMock() self.endpoint.event_converter = mock.MagicMock()
self.endpoint.event_converter.to_event.return_value = mock.MagicMock( self.endpoint.event_converter.to_event.return_value = mock.MagicMock(
event_type='test.test') event_type='test.test')
@mock.patch('ceilometer.event.endpoint.LOG')
def test_event_not_implemented(self, log):
re = self.mock_dispatcher.record_events
re.side_effect = ceilometer.NotImplementedError
message = {'event_type': "foo", 'message_id': "abc"}
ret = self.endpoint.process_notification(message)
log.warn.assert_called_once_with(
'Event is not implemented with the storage backend')
self.assertEqual(oslo.messaging.NotificationResult.HANDLED, ret)
def test_message_to_event(self): def test_message_to_event(self):
self.endpoint.info(TEST_NOTICE_CTXT, 'compute.vagrant-precise', self.endpoint.info(TEST_NOTICE_CTXT, 'compute.vagrant-precise',
'compute.instance.create.end', 'compute.instance.create.end',
TEST_NOTICE_PAYLOAD, TEST_NOTICE_METADATA) TEST_NOTICE_PAYLOAD, TEST_NOTICE_METADATA)
def test_message_to_event_duplicate(self):
self.mock_dispatcher.record_events.return_value = [
(models.Event.DUPLICATE, object())]
message = {'event_type': "foo", 'message_id': "abc"}
self.endpoint.process_notification(message) # Should return silently.
def test_message_to_event_bad_event(self): def test_message_to_event_bad_event(self):
self.CONF.set_override("ack_on_event_error", False, self.CONF.set_override("ack_on_event_error", False,
group="notification") group="notification")
self.mock_dispatcher.record_events.return_value = [ self.mock_pm.publisher.side_effect = Exception
(models.Event.UNKNOWN_PROBLEM, object())]
message = {'event_type': "foo", 'message_id': "abc"} message = {'event_type': "foo", 'message_id': "abc"}
ret = self.endpoint.process_notification(message) ret = self.endpoint.process_notification(message)
self.assertEqual(oslo.messaging.NotificationResult.REQUEUE, ret) self.assertEqual(oslo.messaging.NotificationResult.REQUEUE, ret)

View File

@@ -20,6 +20,7 @@ import uuid
from oslo.utils import netutils from oslo.utils import netutils
from ceilometer.event.storage import models as event
from ceilometer.publisher import direct from ceilometer.publisher import direct
from ceilometer import sample from ceilometer import sample
from ceilometer.tests import db as tests_db from ceilometer.tests import db as tests_db
@@ -80,3 +81,22 @@ class TestDirectPublisher(tests_db.TestBase,
self.assertEqual(3, len(meters), 'There should be 3 samples') self.assertEqual(3, len(meters), 'There should be 3 samples')
self.assertEqual(['alpha', 'beta', 'gamma'], names) self.assertEqual(['alpha', 'beta', 'gamma'], names)
class TestEventDirectPublisher(tests_db.TestBase,
tests_db.MixinTestsWithBackendScenarios):
test_data = [event.Event(message_id=str(uuid.uuid4()),
event_type='event_%d' % i,
generated=datetime.datetime.utcnow(),
traits=[])
for i in range(0, 5)]
def test_direct_publisher(self, ):
parsed_url = netutils.urlsplit('direct://')
publisher = direct.DirectPublisher(parsed_url)
publisher.publish_events(None, self.test_data)
e_types = list(self.event_conn.get_event_types())
self.assertEqual(5, len(e_types))
self.assertEqual(['event_%d' % i for i in range(0, 5)], e_types)

View File

@@ -139,6 +139,7 @@ class TestNotification(tests_base.BaseTestCase):
self.assertNotEqual(self.fake_event_endpoint, self.assertNotEqual(self.fake_event_endpoint,
self.srv.listeners[0].dispatcher.endpoints[0]) self.srv.listeners[0].dispatcher.endpoints[0])
@mock.patch('ceilometer.pipeline.setup_event_pipeline', mock.MagicMock())
def test_process_notification_with_events(self): def test_process_notification_with_events(self):
self.CONF.set_override("store_events", True, group="notification") self.CONF.set_override("store_events", True, group="notification")
self._do_process_notification_manager_start() self._do_process_notification_manager_start()
@@ -146,21 +147,6 @@ class TestNotification(tests_base.BaseTestCase):
self.assertEqual(self.fake_event_endpoint, self.assertEqual(self.fake_event_endpoint,
self.srv.listeners[0].dispatcher.endpoints[0]) self.srv.listeners[0].dispatcher.endpoints[0])
@mock.patch('ceilometer.event.converter.get_config_file',
mock.MagicMock(return_value=None))
@mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock())
@mock.patch.object(oslo.messaging.MessageHandlingServer, 'start',
mock.MagicMock())
def test_event_dispatcher_loaded(self):
self.CONF.set_override("store_events", True, group="notification")
with mock.patch.object(self.srv,
'_get_notifications_manager') as get_nm:
get_nm.side_effect = self.fake_get_notifications_manager
self.srv.start()
self.assertEqual(2, len(self.srv.listeners[0].dispatcher.endpoints))
event_endpoint = self.srv.listeners[0].dispatcher.endpoints[0]
self.assertEqual(1, len(list(event_endpoint.dispatcher_manager)))
@mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock())
@mock.patch.object(oslo.messaging.MessageHandlingServer, 'start', @mock.patch.object(oslo.messaging.MessageHandlingServer, 'start',
mock.MagicMock()) mock.MagicMock())
@@ -193,13 +179,32 @@ class BaseRealNotification(tests_base.BaseTestCase):
'transformers': [], 'transformers': [],
'publishers': ['test://'], 'publishers': ['test://'],
}]) }])
self.expected_samples = 2 self.expected_samples = 2
pipeline_cfg_file = fileutils.write_to_tempfile(content=pipeline, pipeline_cfg_file = fileutils.write_to_tempfile(content=pipeline,
prefix="pipeline", prefix="pipeline",
suffix="yaml") suffix="yaml")
self.CONF.set_override("pipeline_cfg_file", pipeline_cfg_file) self.CONF.set_override("pipeline_cfg_file", pipeline_cfg_file)
self.CONF.set_override("store_events", True, group="notification")
ev_pipeline = yaml.dump({
'sources': [{
'name': 'test_event',
'events': '*',
'sinks': ['test_sink']
}],
'sinks': [{
'name': 'test_sink',
'publishers': ['test://']
}]
})
self.expected_events = 1
ev_pipeline_cfg_file = fileutils.write_to_tempfile(
content=ev_pipeline, prefix="event_pipeline", suffix="yaml")
self.CONF.set_override("event_pipeline_cfg_file", ev_pipeline_cfg_file)
self.CONF.set_override(
"definitions_cfg_file",
self.path_get('etc/ceilometer/event_definitions.yaml'),
group='event')
self.publisher = test_publisher.TestPublisher("") self.publisher = test_publisher.TestPublisher("")
def _check_notification_service(self): def _check_notification_service(self):
@@ -211,7 +216,8 @@ class BaseRealNotification(tests_base.BaseTestCase):
TEST_NOTICE_PAYLOAD) TEST_NOTICE_PAYLOAD)
start = timeutils.utcnow() start = timeutils.utcnow()
while timeutils.delta_seconds(start, timeutils.utcnow()) < 600: while timeutils.delta_seconds(start, timeutils.utcnow()) < 600:
if len(self.publisher.samples) >= self.expected_samples: if (len(self.publisher.samples) >= self.expected_samples and
len(self.publisher.events) >= self.expected_events):
break break
eventlet.sleep(0) eventlet.sleep(0)
@@ -219,6 +225,7 @@ class BaseRealNotification(tests_base.BaseTestCase):
resources = list(set(s.resource_id for s in self.publisher.samples)) resources = list(set(s.resource_id for s in self.publisher.samples))
self.assertEqual(self.expected_samples, len(self.publisher.samples)) self.assertEqual(self.expected_samples, len(self.publisher.samples))
self.assertEqual(self.expected_events, len(self.publisher.events))
self.assertEqual(["9f9d01b9-4a58-4271-9e27-398b21ab20d1"], resources) self.assertEqual(["9f9d01b9-4a58-4271-9e27-398b21ab20d1"], resources)
@@ -263,7 +270,7 @@ class TestRealNotificationHA(BaseRealNotification):
def test_reset_listeners_on_refresh(self): def test_reset_listeners_on_refresh(self):
self.srv.start() self.srv.start()
self.assertEqual(1, len(self.srv.pipeline_listeners)) self.assertEqual(2, len(self.srv.pipeline_listeners))
self.srv._refresh_agent(None) self.srv._refresh_agent(None)
self.assertEqual(1, len(self.srv.pipeline_listeners)) self.assertEqual(2, len(self.srv.pipeline_listeners))
self.srv.stop() self.srv.stop()

View File

@@ -261,6 +261,10 @@ ceilometer.publisher =
file = ceilometer.publisher.file:FilePublisher file = ceilometer.publisher.file:FilePublisher
direct = ceilometer.publisher.direct:DirectPublisher direct = ceilometer.publisher.direct:DirectPublisher
ceilometer.event.publisher =
test = ceilometer.publisher.test:TestPublisher
direct = ceilometer.publisher.direct:DirectPublisher
ceilometer.alarm.evaluator = ceilometer.alarm.evaluator =
threshold = ceilometer.alarm.evaluator.threshold:ThresholdEvaluator threshold = ceilometer.alarm.evaluator.threshold:ThresholdEvaluator
combination = ceilometer.alarm.evaluator.combination:CombinationEvaluator combination = ceilometer.alarm.evaluator.combination:CombinationEvaluator