Added notification_level config option

In this changeset, I implemented the notification_level parameter
that allows you to set the level minimum level of notification to
be emitted by Watcher.

Partially Implements: blueprint watcher-notifications-ovo

Change-Id: I78b30ceb3ee7606078a87beb3a4ce5d9568af1e0
This commit is contained in:
Vincent Françoise 2016-11-14 14:54:42 +01:00
parent cdda06c08c
commit 4bebf882d9
6 changed files with 103 additions and 9 deletions

View File

@ -399,3 +399,8 @@ class NotSoftDeletedStateError(WatcherException):
class NegativeLimitError(WatcherException):
msg_fmt = _("Limit should be positive")
class NotificationPayloadError(WatcherException):
_msg_fmt = _("Payload not populated when trying to send notification "
"\"%(class_name)s\"")

View File

@ -72,8 +72,12 @@ def init(conf):
aliases=TRANSPORT_ALIASES)
serializer = RequestContextSerializer(JsonPayloadSerializer())
NOTIFIER = messaging.Notifier(NOTIFICATION_TRANSPORT,
serializer=serializer)
if not conf.notification_level:
NOTIFIER = messaging.Notifier(
NOTIFICATION_TRANSPORT, serializer=serializer, driver='noop')
else:
NOTIFIER = messaging.Notifier(NOTIFICATION_TRANSPORT,
serializer=serializer)
def initialized():

View File

@ -36,6 +36,7 @@ from watcher.common import rpc
from watcher.common import scheduling
from watcher import objects
from watcher.objects import base
from watcher.objects import fields as wfields
from watcher import opts
from watcher import version
@ -64,6 +65,18 @@ service_opts = [
cfg.CONF.register_opts(service_opts)
NOTIFICATION_OPTS = [
cfg.StrOpt('notification_level',
choices=[''] + list(wfields.NotificationPriority.ALL),
default=wfields.NotificationPriority.INFO,
help=_('Specifies the minimum level for which to send '
'notifications. If not set, no notifications will '
'be sent. The default is for this option to be at the '
'`INFO` level.'))
]
cfg.CONF.register_opts(NOTIFICATION_OPTS)
CONF = cfg.CONF
LOG = log.getLogger(__name__)

View File

@ -103,14 +103,13 @@ class BaseWatcherEnum(Enum):
class NotificationPriority(BaseWatcherEnum):
CRITICAL = 'critical'
DEBUG = 'debug'
INFO = 'info'
WARNING = 'warning'
ERROR = 'error'
SAMPLE = 'sample'
WARNING = 'warn'
CRITICAL = 'critical'
ALL = (CRITICAL, DEBUG, INFO, ERROR, SAMPLE, WARNING)
ALL = (DEBUG, INFO, WARNING, ERROR, CRITICAL)
class NotificationPhase(BaseWatcherEnum):

View File

@ -12,10 +12,24 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from watcher.common import exception
from watcher.common import rpc
from watcher.objects import base
from watcher.objects import fields as wfields
CONF = cfg.CONF
# Definition of notification levels in increasing order of severity
NOTIFY_LEVELS = {
wfields.NotificationPriority.DEBUG: 0,
wfields.NotificationPriority.INFO: 1,
wfields.NotificationPriority.WARNING: 2,
wfields.NotificationPriority.ERROR: 3,
wfields.NotificationPriority.CRITICAL: 4
}
@base.WatcherObjectRegistry.register_if(False)
class NotificationObject(base.WatcherObject):
@ -125,6 +139,20 @@ class NotificationBase(NotificationObject):
'publisher': wfields.ObjectField('NotificationPublisher'),
}
def _should_notify(self):
"""Determine whether the notification should be sent.
A notification is sent when the level of the notification is
greater than or equal to the level specified in the
configuration, in the increasing order of DEBUG, INFO, WARNING,
ERROR, CRITICAL.
:return: True if notification should be sent, False otherwise.
"""
if not CONF.notification_level:
return False
return (NOTIFY_LEVELS[self.priority] >=
NOTIFY_LEVELS[CONF.notification_level])
def _emit(self, context, event_type, publisher_id, payload):
notifier = rpc.get_notifier(publisher_id)
notify = getattr(notifier, self.priority)
@ -132,8 +160,11 @@ class NotificationBase(NotificationObject):
def emit(self, context):
"""Send the notification."""
assert self.payload.populated
if not self._should_notify():
return
if not self.payload.populated:
raise exception.NotificationPayloadError(
class_name=self.__class__.__name__)
# Note(gibi): notification payload will be a newly populated object
# therefore every field of it will look changed so this does not carry
# any extra information so we drop this from the payload.

View File

@ -17,6 +17,7 @@ import collections
import mock
from oslo_versionedobjects import fixture
from watcher.common import exception
from watcher.common import rpc
from watcher.objects import base
from watcher.objects import fields as wfields
@ -136,6 +137,46 @@ class TestNotificationBase(testbase.TestCase):
expected_event_type='test_object.update.start',
expected_payload=self.expected_payload)
@mock.patch.object(rpc, 'NOTIFIER')
def test_no_emit_notifs_disabled(self, mock_notifier):
# Make sure notifications aren't emitted when notification_level
# isn't defined, indicating notifications should be disabled
self.config(notification_level=None)
notif = self.TestNotification(
event_type=notificationbase.EventType(
object='test_object',
action=wfields.NotificationAction.UPDATE,
phase=wfields.NotificationPhase.START),
publisher=notificationbase.NotificationPublisher(
host='fake-host', binary='watcher-fake'),
priority=wfields.NotificationPriority.INFO,
payload=self.payload)
mock_context = mock.Mock()
notif.emit(mock_context)
self.assertFalse(mock_notifier.called)
@mock.patch.object(rpc, 'NOTIFIER')
def test_no_emit_level_too_low(self, mock_notifier):
# Make sure notification doesn't emit when set notification
# level < config level
self.config(notification_level='warning')
notif = self.TestNotification(
event_type=notificationbase.EventType(
object='test_object',
action=wfields.NotificationAction.UPDATE,
phase=wfields.NotificationPhase.START),
publisher=notificationbase.NotificationPublisher(
host='fake-host', binary='watcher-fake'),
priority=wfields.NotificationPriority.INFO,
payload=self.payload)
mock_context = mock.Mock()
notif.emit(mock_context)
self.assertFalse(mock_notifier.called)
@mock.patch.object(rpc, 'NOTIFIER')
def test_emit_event_type_without_phase(self, mock_notifier):
noti = self.TestNotification(
@ -171,7 +212,8 @@ class TestNotificationBase(testbase.TestCase):
payload=non_populated_payload)
mock_context = mock.Mock()
self.assertRaises(AssertionError, noti.emit, mock_context)
self.assertRaises(exception.NotificationPayloadError,
noti.emit, mock_context)
self.assertFalse(mock_notifier.called)
@mock.patch.object(rpc, 'NOTIFIER')