Storing events via dispatchers
Removes the database handle from CollectorService and moves the logic for storing events to Dispatcher classes. Change-Id: I22cf02685e3a33a9014d65d8d32a5b585dabe185 Fixes: bug #1204520
This commit is contained in:
parent
4e99c1392e
commit
fd61f630e9
@ -29,3 +29,7 @@ class Base(object):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def record_metering_data(self, context, data):
|
def record_metering_data(self, context, data):
|
||||||
"""Recording metering data interface."""
|
"""Recording metering data interface."""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def record_events(self, events):
|
||||||
|
"""Recording events interface."""
|
||||||
|
@ -70,3 +70,8 @@ class DatabaseDispatcher(dispatcher.Base):
|
|||||||
LOG.warning(
|
LOG.warning(
|
||||||
'message signature invalid, discarding message: %r',
|
'message signature invalid, discarding message: %r',
|
||||||
meter)
|
meter)
|
||||||
|
|
||||||
|
def record_events(self, events):
|
||||||
|
if not isinstance(events, list):
|
||||||
|
events = [events]
|
||||||
|
self.storage_conn.record_events(events)
|
||||||
|
@ -79,3 +79,7 @@ class FileDispatcher(dispatcher.Base):
|
|||||||
def record_metering_data(self, context, data):
|
def record_metering_data(self, context, data):
|
||||||
if self.log:
|
if self.log:
|
||||||
self.log.info(data)
|
self.log.info(data)
|
||||||
|
|
||||||
|
def record_events(self, events):
|
||||||
|
if self.log:
|
||||||
|
self.log.info(events)
|
||||||
|
@ -21,6 +21,7 @@ from oslo.config import cfg
|
|||||||
import socket
|
import socket
|
||||||
from stevedore import extension
|
from stevedore import extension
|
||||||
from stevedore import named
|
from stevedore import named
|
||||||
|
import sys
|
||||||
|
|
||||||
from ceilometer.service import prepare_service
|
from ceilometer.service import prepare_service
|
||||||
from ceilometer.openstack.common import context
|
from ceilometer.openstack.common import context
|
||||||
@ -112,10 +113,6 @@ class CollectorService(rpc_service.Service):
|
|||||||
COLLECTOR_NAMESPACE = 'ceilometer.collector'
|
COLLECTOR_NAMESPACE = 'ceilometer.collector'
|
||||||
DISPATCHER_NAMESPACE = 'ceilometer.dispatcher'
|
DISPATCHER_NAMESPACE = 'ceilometer.dispatcher'
|
||||||
|
|
||||||
def __init__(self, host, topic, manager=None):
|
|
||||||
super(CollectorService, self).__init__(host, topic, manager)
|
|
||||||
self.storage_conn = storage.get_connection(cfg.CONF)
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
super(CollectorService, self).start()
|
super(CollectorService, self).start()
|
||||||
# Add a dummy thread to have wait() working
|
# Add a dummy thread to have wait() working
|
||||||
@ -143,17 +140,16 @@ class CollectorService(rpc_service.Service):
|
|||||||
self.COLLECTOR_NAMESPACE)
|
self.COLLECTOR_NAMESPACE)
|
||||||
self.notification_manager.map(self._setup_subscription)
|
self.notification_manager.map(self._setup_subscription)
|
||||||
|
|
||||||
# Load all configured dispatchers
|
LOG.debug('loading dispatchers from %s',
|
||||||
self.dispatchers = []
|
self.DISPATCHER_NAMESPACE)
|
||||||
for dispatcher in named.NamedExtensionManager(
|
self.dispatcher_manager = named.NamedExtensionManager(
|
||||||
namespace=self.DISPATCHER_NAMESPACE,
|
namespace=self.DISPATCHER_NAMESPACE,
|
||||||
names=cfg.CONF.collector.dispatcher,
|
names=cfg.CONF.collector.dispatcher,
|
||||||
invoke_on_load=True,
|
invoke_on_load=True,
|
||||||
invoke_args=[cfg.CONF]):
|
invoke_args=[cfg.CONF])
|
||||||
if dispatcher.obj:
|
if not list(self.dispatcher_manager):
|
||||||
self.dispatchers.append(dispatcher.obj)
|
LOG.warning('Failed to load any dispatchers for %s',
|
||||||
|
self.DISPATCHER_NAMESPACE)
|
||||||
LOG.info('dispatchers loaded %s' % str(self.dispatchers))
|
|
||||||
|
|
||||||
# Set ourselves up as a separate worker for the metering data,
|
# Set ourselves up as a separate worker for the metering data,
|
||||||
# since the default for service is to use create_consumer().
|
# since the default for service is to use create_consumer().
|
||||||
@ -184,8 +180,9 @@ class CollectorService(rpc_service.Service):
|
|||||||
(topic, exchange_topic.exchange))
|
(topic, exchange_topic.exchange))
|
||||||
|
|
||||||
def record_metering_data(self, context, data):
|
def record_metering_data(self, context, data):
|
||||||
for dispatcher in self.dispatchers:
|
self.dispatcher_manager.map(self._record_metering_data_for_ext,
|
||||||
dispatcher.record_metering_data(context, data)
|
context=context,
|
||||||
|
data=data)
|
||||||
|
|
||||||
def process_notification(self, notification):
|
def process_notification(self, notification):
|
||||||
"""Make a notification processed by an handler."""
|
"""Make a notification processed by an handler."""
|
||||||
@ -239,12 +236,22 @@ class CollectorService(rpc_service.Service):
|
|||||||
traits = [trait for trait in all_traits if trait.value is not None]
|
traits = [trait for trait in all_traits if trait.value is not None]
|
||||||
|
|
||||||
event = models.Event(event_name, when, traits)
|
event = models.Event(event_name, when, traits)
|
||||||
try:
|
|
||||||
self.storage_conn.record_events([event, ])
|
exc_info = None
|
||||||
except Exception as err:
|
for dispatcher in self.dispatcher_manager:
|
||||||
LOG.exception(_("Unable to store events: %s"), err)
|
try:
|
||||||
# By re-raising we avoid ack()'ing the message.
|
dispatcher.obj.record_events(event)
|
||||||
raise
|
except Exception:
|
||||||
|
LOG.exception('Error while saving events with dispatcher %s',
|
||||||
|
dispatcher)
|
||||||
|
exc_info = sys.exc_info()
|
||||||
|
# Don't ack the message if any of the dispatchers fail
|
||||||
|
if exc_info:
|
||||||
|
raise exc_info[1], None, exc_info[2]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _record_metering_data_for_ext(ext, context, data):
|
||||||
|
ext.obj.record_metering_data(context, data)
|
||||||
|
|
||||||
def _process_notification_for_ext(self, ext, notification):
|
def _process_notification_for_ext(self, ext, notification):
|
||||||
with self.pipeline_manager.publisher(context.get_admin_context()) as p:
|
with self.pipeline_manager.publisher(context.get_admin_context()) as p:
|
||||||
|
@ -179,11 +179,6 @@ class TestCollectorService(TestCollector):
|
|||||||
self.srv = service.CollectorService('the-host', 'the-topic')
|
self.srv = service.CollectorService('the-host', 'the-topic')
|
||||||
self.ctx = None
|
self.ctx = None
|
||||||
|
|
||||||
def test_service_has_storage_conn(self):
|
|
||||||
# Test an unmocked default CollectorService
|
|
||||||
srv = service.CollectorService('the-host', 'the-topic')
|
|
||||||
self.assertIsNotNone(srv.storage_conn)
|
|
||||||
|
|
||||||
@patch('ceilometer.pipeline.setup_pipeline', MagicMock())
|
@patch('ceilometer.pipeline.setup_pipeline', MagicMock())
|
||||||
def test_init_host(self):
|
def test_init_host(self):
|
||||||
# If we try to create a real RPC connection, init_host() never
|
# If we try to create a real RPC connection, init_host() never
|
||||||
@ -231,27 +226,41 @@ class TestCollectorService(TestCollector):
|
|||||||
timeutils.set_time_override(now)
|
timeutils.set_time_override(now)
|
||||||
message = {'event_type': "foo", 'message_id': "abc"}
|
message = {'event_type': "foo", 'message_id': "abc"}
|
||||||
|
|
||||||
self.srv.storage_conn = MagicMock()
|
mock_dispatcher = MagicMock()
|
||||||
|
self.srv.dispatcher_manager = test_manager.TestExtensionManager(
|
||||||
|
[extension.Extension('test',
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
mock_dispatcher
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
with patch('ceilometer.collector.service.LOG') as mylog:
|
with patch('ceilometer.collector.service.LOG') as mylog:
|
||||||
self.srv._message_to_event(message)
|
self.srv._message_to_event(message)
|
||||||
self.assertFalse(mylog.exception.called)
|
self.assertFalse(mylog.exception.called)
|
||||||
events = self.srv.storage_conn.record_events.call_args[0]
|
events = mock_dispatcher.record_events.call_args[0]
|
||||||
self.assertEqual(1, len(events))
|
self.assertEqual(1, len(events))
|
||||||
event = events[0][0]
|
event = events[0]
|
||||||
self.assertEqual("foo", event.event_name)
|
self.assertEqual("foo", event.event_name)
|
||||||
self.assertEqual(now, event.generated)
|
self.assertEqual(now, event.generated)
|
||||||
self.assertEqual(1, len(event.traits))
|
self.assertEqual(1, len(event.traits))
|
||||||
|
|
||||||
def test_message_to_event_bad_save(self):
|
def test_message_to_event_bad_save(self):
|
||||||
cfg.CONF.set_override("store_events", True, group="collector")
|
cfg.CONF.set_override("store_events", True, group="collector")
|
||||||
self.srv.storage_conn = MagicMock()
|
mock_dispatcher = MagicMock()
|
||||||
self.srv.storage_conn.record_events.side_effect = MyException("Boom")
|
self.srv.dispatcher_manager = test_manager.TestExtensionManager(
|
||||||
|
[extension.Extension('test',
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
mock_dispatcher
|
||||||
|
),
|
||||||
|
])
|
||||||
|
mock_dispatcher.record_events.side_effect = MyException("Boom")
|
||||||
message = {'event_type': "foo", 'message_id': "abc"}
|
message = {'event_type': "foo", 'message_id': "abc"}
|
||||||
try:
|
try:
|
||||||
self.srv._message_to_event(message)
|
self.srv._message_to_event(message)
|
||||||
self.fail("failing save should raise")
|
self.fail("failing save should raise")
|
||||||
except MyException:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_extract_when(self):
|
def test_extract_when(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user