Migrate a notifier patch from common:

commit c767e9beffe4b826eac869ce7e2eef2cc1499bbe
Author: Andrew Bogott <abogott@wikimedia.org>
Date: Thu Jul 19 03:34:31 2012 -0500

Add multiple-driver support to the notifier api.

Move all of the functionality previously provided by the list_notifier
into the basic notifier api. Move and restructure tests accordingly.

Remove the list_notifier file and test file.

For bug 1025820

Change-Id: Icadaafe8bc312942249272873d9a4a8f1aa49a94
This commit is contained in:
Andrew Bogott
2012-08-03 20:15:54 -05:00
parent 2e48275668
commit 20470bc8cd
7 changed files with 71 additions and 160 deletions

View File

@@ -247,10 +247,9 @@ class JSONFormatter(logging.Formatter):
class PublishErrorsHandler(logging.Handler):
def emit(self, record):
if 'list_notifier_drivers' in CONF:
if ('nova.openstack.common.notifier.log_notifier' in
CONF.list_notifier_drivers):
return
if ('nova.openstack.common.notifier.log_notifier' in
CONF.notification_driver):
return
notifier.api.notify(None, 'error.publisher',
'error_notification',
notifier.api.ERROR,

View File

@@ -28,16 +28,17 @@ from nova.openstack.common import timeutils
LOG = logging.getLogger(__name__)
notifier_opts = [
cfg.StrOpt('notification_driver',
default='nova.openstack.common.notifier.no_op_notifier',
help='Default driver for sending notifications'),
cfg.MultiStrOpt('notification_driver',
default=[],
deprecated_name='list_notifier_drivers',
help='Driver or drivers to handle sending notifications'),
cfg.StrOpt('default_notification_level',
default='INFO',
help='Default notification level for outgoing notifications'),
cfg.StrOpt('default_publisher_id',
default='$host',
help='Default publisher_id for outgoing notifications'),
]
]
CONF = cfg.CONF
CONF.register_opts(notifier_opts)
@@ -122,21 +123,60 @@ def notify(context, publisher_id, event_type, priority, payload):
"""
if priority not in log_levels:
raise BadPriorityException(
_('%s not in valid priorities') % priority)
_('%s not in valid priorities') % priority)
# Ensure everything is JSON serializable.
payload = jsonutils.to_primitive(payload, convert_instances=True)
driver = importutils.import_module(CONF.notification_driver)
msg = dict(message_id=str(uuid.uuid4()),
publisher_id=publisher_id,
event_type=event_type,
priority=priority,
payload=payload,
timestamp=str(timeutils.utcnow()))
try:
driver.notify(context, msg)
except Exception, e:
LOG.exception(_("Problem '%(e)s' attempting to "
"send to notification system. Payload=%(payload)s") %
locals())
publisher_id=publisher_id,
event_type=event_type,
priority=priority,
payload=payload,
timestamp=str(timeutils.utcnow()))
for driver in _get_drivers():
try:
driver.notify(context, msg)
except Exception, e:
LOG.exception(_("Problem '%(e)s' attempting to "
"send to notification system. Payload=%(payload)s") %
locals())
_drivers = None
def _get_drivers():
"""Instantiate, cache, and return drivers based on the CONF."""
global _drivers
if _drivers is None:
_drivers = {}
for notification_driver in CONF.notification_driver:
add_driver(notification_driver)
return _drivers.values()
def add_driver(notification_driver):
"""Add a notification driver at runtime."""
# Make sure the driver list is initialized.
_get_drivers()
if isinstance(notification_driver, basestring):
# Load and add
try:
driver = importutils.import_module(notification_driver)
_drivers[notification_driver] = driver
except ImportError as e:
LOG.exception(_("Failed to load notifier %s. "
"These notifications will not be sent.") %
notification_driver)
else:
# Driver is already loaded; just add the object.
_drivers[notification_driver] = notification_driver
def _reset_drivers():
"""Used by unit tests to reset the drivers."""
global _drivers
_drivers = None

View File

@@ -1,117 +0,0 @@
# Copyright 2011 OpenStack LLC.
# All Rights Reserved.
#
# 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 nova.openstack.common import cfg
from nova.openstack.common.gettextutils import _
from nova.openstack.common import importutils
from nova.openstack.common import log as logging
list_notifier_drivers_opt = cfg.MultiStrOpt('list_notifier_drivers',
default=['nova.openstack.common.notifier.no_op_notifier'],
help='List of drivers to send notifications')
CONF = cfg.CONF
CONF.register_opt(list_notifier_drivers_opt)
LOG = logging.getLogger(__name__)
drivers = None
class ImportFailureNotifier(object):
"""Noisily re-raises some exception over-and-over when notify is called."""
def __init__(self, exception):
self.exception = exception
def notify(self, context, message):
raise self.exception
def _get_drivers():
"""Instantiates and returns drivers based on the flag values."""
global drivers
if drivers is None:
drivers = []
for notification_driver in CONF.list_notifier_drivers:
try:
drivers.append(importutils.import_module(notification_driver))
except ImportError as e:
drivers.append(ImportFailureNotifier(e))
return drivers
def add_driver(notification_driver):
"""Add a notification driver at runtime."""
# Make sure the driver list is initialized.
_get_drivers()
if isinstance(notification_driver, basestring):
# Load and add
try:
drivers.append(importutils.import_module(notification_driver))
except ImportError as e:
drivers.append(ImportFailureNotifier(e))
else:
# Driver is already loaded; just add the object.
drivers.append(notification_driver)
def _object_name(obj):
name = []
if hasattr(obj, '__module__'):
name.append(obj.__module__)
if hasattr(obj, '__name__'):
name.append(obj.__name__)
else:
name.append(obj.__class__.__name__)
return '.'.join(name)
def remove_driver(notification_driver):
"""Remove a notification driver at runtime."""
# Make sure the driver list is initialized.
_get_drivers()
removed = False
if notification_driver in drivers:
# We're removing an object. Easy.
drivers.remove(notification_driver)
removed = True
else:
# We're removing a driver by name. Search for it.
for driver in drivers:
if _object_name(driver) == notification_driver:
drivers.remove(driver)
removed = True
if not removed:
raise ValueError("Cannot remove; %s is not in list" %
notification_driver)
def notify(context, message):
"""Passes notification to multiple notifiers in a list."""
for driver in _get_drivers():
try:
driver.notify(context, message)
except Exception as e:
LOG.exception(_("Problem '%(e)s' attempting to send to "
"notification driver %(driver)s."), locals())
def _reset_drivers():
"""Used by unit tests to reset the drivers."""
global drivers
drivers = None

View File

@@ -14,7 +14,6 @@
# under the License.
from nova.openstack.common import log as logging
from nova.openstack.common.notifier import list_notifier
LOG = logging.getLogger(__name__)

View File

@@ -19,7 +19,7 @@ import pkg_resources
from nova.openstack.common import cfg
from nova.openstack.common import log as logging
from nova.openstack.common.notifier import list_notifier
from nova.openstack.common.notifier import api as notifier_api
CONF = cfg.CONF
@@ -53,17 +53,6 @@ class PluginManager(object):
self._service_name = service_name
self.plugins = []
def _force_use_list_notifier(self):
if (CONF.notification_driver !=
'nova.openstack.common.notifier.list_notifier'):
if not hasattr(CONF, "list_notifier_drivers"):
CONF.list_notifier_drivers = []
old_notifier = CONF.notification_driver
drvstring = 'nova.openstack.common.notifier.list_notifier'
CONF.notification_driver = drvstring
if old_notifier:
list_notifier.add_driver(old_notifier)
def load_plugins(self):
self.plugins = []
@@ -77,16 +66,10 @@ class PluginManager(object):
LOG.error(_("Failed to load plugin %(plug)s: %(exc)s") %
{'plug': entrypoint, 'exc': exc})
# See if we need to turn on the list notifier
for plugin in self.plugins:
if plugin.notifiers:
self._force_use_list_notifier()
break
# Register individual notifiers.
for plugin in self.plugins:
for notifier in plugin.notifiers:
list_notifier.add_driver(notifier)
notifier_api.add_driver(notifier)
def plugin_extension_factory(self, ext_mgr):
for plugin in self.plugins:

View File

@@ -30,6 +30,7 @@ from nova import exception
from nova import flags
from nova.openstack.common import importutils
from nova.openstack.common import log as logging
from nova.openstack.common.notifier import api as notifier_api
from nova.openstack.common.notifier import test_notifier
from nova.openstack.common import rpc
import nova.policy
@@ -50,7 +51,7 @@ class VolumeTestCase(test.TestCase):
self.compute = importutils.import_object(FLAGS.compute_manager)
self.flags(compute_driver='nova.virt.fake.FakeDriver')
self.stubs.Set(nova.flags.FLAGS, 'notification_driver',
'nova.openstack.common.notifier.test_notifier')
['nova.openstack.common.notifier.test_notifier'])
self.volume = importutils.import_object(FLAGS.volume_manager)
self.context = context.get_admin_context()
instance = db.instance_create(self.context, {})
@@ -60,6 +61,7 @@ class VolumeTestCase(test.TestCase):
def tearDown(self):
db.instance_destroy(self.context, self.instance_uuid)
notifier_api._reset_drivers()
super(VolumeTestCase, self).tearDown()
@staticmethod

View File

@@ -22,6 +22,7 @@ from nova import db
from nova import flags
from nova.openstack.common import importutils
from nova.openstack.common import log as logging
from nova.openstack.common.notifier import api as notifier_api
from nova.openstack.common.notifier import test_notifier
from nova import test
from nova.volume import utils as volume_utils
@@ -39,7 +40,7 @@ class UsageInfoTestCase(test.TestCase):
stub_network=True,
host='fake')
self.stubs.Set(flags.FLAGS, 'notification_driver',
'nova.openstack.common.notifier.test_notifier')
['nova.openstack.common.notifier.test_notifier'])
self.volume = importutils.import_object(FLAGS.volume_manager)
self.user_id = 'fake'
self.project_id = 'fake'
@@ -48,6 +49,10 @@ class UsageInfoTestCase(test.TestCase):
self.context = context.RequestContext(self.user_id, self.project_id)
test_notifier.NOTIFICATIONS = []
def tearDown(self):
notifier_api._reset_drivers()
super(UsageInfoTestCase, self).tearDown()
def _create_volume(self, params={}):
"""Create a test volume"""
vol = {}