From 77c7a710fc0eb1f419db650e3aa958c31b3bd74f Mon Sep 17 00:00:00 2001 From: Mario Villaplana Date: Thu, 1 Sep 2016 20:44:49 +0000 Subject: [PATCH] Replace "phase" with "status" in notification base The notification spec is being updated to rename "phase" to "status" and make it a required field. This updates the notification base code and docs to reflect this modification. Version for the notification EventType base class is also bumped. Change-Id: I11b6ea44a01d2a830b592784ccc63a1de9bf2ec1 Depends-On: Ib25bd5a3a600c13f7b265d86ad253af3dfa5552f --- doc/source/dev/notifications.rst | 20 +++++++++++-- ironic/common/exception.py | 5 ++++ ironic/objects/fields.py | 4 +++ ironic/objects/notification.py | 17 ++++++----- .../tests/unit/objects/test_notification.py | 29 +++++++++++-------- ironic/tests/unit/objects/test_objects.py | 2 +- 6 files changed, 55 insertions(+), 22 deletions(-) diff --git a/doc/source/dev/notifications.rst b/doc/source/dev/notifications.rst index 9cc249be98..aee82e5bf9 100644 --- a/doc/source/dev/notifications.rst +++ b/doc/source/dev/notifications.rst @@ -147,14 +147,24 @@ in the ironic notification base classes) and emit it:: notify = ExampleNotification( event_type=notification.EventType(object='example_obj', - action='do_something', phase='start'), + action='do_something', status='start'), publisher=notification.NotificationPublisher(service='conductor', host='cond-hostname01'), level=fields.NotificationLevel.DEBUG, payload=my_notify_payload) notify.emit(context) -This will send the following notification over the message bus:: +When specifying the event_type, ``object`` will specify the object being acted +on, ``action`` will be a string describing what action is being performed on +that object, and ``status`` will be one of "start", "end", "fail", or +"success". "start" and "end" are used to indicate when actions that are not +immediate begin and succeed. "success" is used to indicate when actions that +are immediate succeed. "fail" is used to indicate when any type of action +fails, regardless of whether it's immediate or not. As a result of specifying +these parameters, event_type will be formatted as +``baremetal...`` on the message bus. + +This example will send the following notification over the message bus:: { "priority": "debug", @@ -171,6 +181,12 @@ This will send the following notification over the message bus:: "publisher_id":"conductor.cond-hostname01" } +Existing notifications +---------------------- + +Descriptions of notifications emitted by ironic will be documented here when +they are added. + .. [1] http://docs.openstack.org/developer/oslo.messaging/notifier.html .. [2] http://docs.openstack.org/developer/oslo.versionedobjects .. [3] https://wiki.openstack.org/wiki/LoggingStandards#Log_level_definitions diff --git a/ironic/common/exception.py b/ironic/common/exception.py index 4352ee8bc8..ce3654de8b 100644 --- a/ironic/common/exception.py +++ b/ironic/common/exception.py @@ -611,6 +611,11 @@ class IncompleteLookup(Invalid): "is required") +class NotificationEventTypeError(IronicException): + _msg_fmt = _('Expected "status" to be one of "start", "end", ' + '"error", or "success", but got "%(status)s"') + + class NotificationSchemaObjectError(IronicException): _msg_fmt = _("Expected object %(obj)s when populating notification payload" " but got object %(source)s") diff --git a/ironic/objects/fields.py b/ironic/objects/fields.py index a61ccff7d5..f3ce4a3033 100644 --- a/ironic/objects/fields.py +++ b/ironic/objects/fields.py @@ -102,6 +102,10 @@ class FlexibleDictField(object_fields.AutoTypedField): super(FlexibleDictField, self)._null(obj, attr) +class EnumField(object_fields.EnumField): + pass + + class NotificationLevel(object_fields.Enum): DEBUG = 'debug' INFO = 'info' diff --git a/ironic/objects/notification.py b/ironic/objects/notification.py index 5dcb009bb8..730d2d02b2 100644 --- a/ironic/objects/notification.py +++ b/ironic/objects/notification.py @@ -35,22 +35,25 @@ class EventType(base.IronicObject): """Defines the event_type to be sent on the wire. An EventType must specify the object being acted on, a string describing - the action being taken on the notification, and the phase of the action, - if applicable. + the action being taken on the notification, and the status of the action. """ # Version 1.0: Initial version - VERSION = '1.0' + # Version 1.1: "phase" field was renamed to "status" and only accepts + # "start", "end", "error", or "success" as valid + VERSION = '1.1' fields = { 'object': fields.StringField(nullable=False), 'action': fields.StringField(nullable=False), - 'phase': fields.StringField(nullable=True) + 'status': fields.EnumField(valid_values=['start', 'end', 'error', + 'success'], + nullable=False) } def to_event_type_field(self): - parts = ['baremetal', self.object, self.action] - if self.obj_attr_is_set('phase') and self.phase is not None: - parts.append(self.phase) + if self.status not in ['start', 'end', 'error', 'success']: + raise exception.NotificationEventTypeError(status=self.status) + parts = ['baremetal', self.object, self.action, self.status] return '.'.join(parts) diff --git a/ironic/tests/unit/objects/test_notification.py b/ironic/tests/unit/objects/test_notification.py index 75808a9ed5..6483bfb851 100644 --- a/ironic/tests/unit/objects/test_notification.py +++ b/ironic/tests/unit/objects/test_notification.py @@ -95,7 +95,7 @@ class TestNotificationBase(test_base.TestCase): payload.populate_schema(test_obj=self.fake_obj) notif = self.TestNotification( event_type=notification.EventType( - object='test_object', action='test', phase='start'), + object='test_object', action='test', status='start'), level=fields.NotificationLevel.DEBUG, publisher=notification.NotificationPublisher( service='ironic-conductor', @@ -132,7 +132,7 @@ class TestNotificationBase(test_base.TestCase): payload.populate_schema(test_obj=self.fake_obj) notif = self.TestNotification( event_type=notification.EventType( - object='test_object', action='test', phase='start'), + object='test_object', action='test', status='start'), level=fields.NotificationLevel.DEBUG, publisher=notification.NotificationPublisher( service='ironic-conductor', @@ -153,7 +153,7 @@ class TestNotificationBase(test_base.TestCase): payload.populate_schema(test_obj=self.fake_obj) notif = self.TestNotification( event_type=notification.EventType( - object='test_object', action='test', phase='start'), + object='test_object', action='test', status='start'), level=fields.NotificationLevel.DEBUG, publisher=notification.NotificationPublisher( service='ironic-conductor', @@ -172,7 +172,7 @@ class TestNotificationBase(test_base.TestCase): an_optional_field=1) notif = self.TestNotification( event_type=notification.EventType( - object='test_object', action='test', phase='start'), + object='test_object', action='test', status='start'), level=fields.NotificationLevel.DEBUG, publisher=notification.NotificationPublisher( service='ironic-conductor', @@ -190,7 +190,7 @@ class TestNotificationBase(test_base.TestCase): payload = self.TestNotificationPayloadEmptySchema(fake_field='123') notif = self.TestNotificationEmptySchema( event_type=notification.EventType( - object='test_object', action='test', phase='fail'), + object='test_object', action='test', status='error'), level=fields.NotificationLevel.ERROR, publisher=notification.NotificationPublisher( service='ironic-conductor', @@ -203,7 +203,7 @@ class TestNotificationBase(test_base.TestCase): self._verify_notification( mock_notifier, mock_context, - expected_event_type='baremetal.test_object.test.fail', + expected_event_type='baremetal.test_object.test.error', expected_payload={ 'ironic_object.name': 'TestNotificationPayloadEmptySchema', 'ironic_object.data': { @@ -230,14 +230,19 @@ class TestNotificationBase(test_base.TestCase): payload.populate_schema, test_obj=test_obj) - def test_event_type_with_phase(self): + def test_event_type_with_status(self): event_type = notification.EventType( - object="some_obj", action="some_action", phase="some_phase") - self.assertEqual("baremetal.some_obj.some_action.some_phase", + object="some_obj", action="some_action", status="success") + self.assertEqual("baremetal.some_obj.some_action.success", event_type.to_event_type_field()) - def test_event_type_without_phase(self): + def test_event_type_without_status_fails(self): event_type = notification.EventType( object="some_obj", action="some_action") - self.assertEqual("baremetal.some_obj.some_action", - event_type.to_event_type_field()) + self.assertRaises(NotImplementedError, + event_type.to_event_type_field) + + def test_event_type_invalid_status_fails(self): + self.assertRaises(ValueError, + notification.EventType, object="some_obj", + action="some_action", status="invalid") diff --git a/ironic/tests/unit/objects/test_objects.py b/ironic/tests/unit/objects/test_objects.py index efbd3b667d..54ea4e4489 100644 --- a/ironic/tests/unit/objects/test_objects.py +++ b/ironic/tests/unit/objects/test_objects.py @@ -410,7 +410,7 @@ expected_object_fingerprints = { 'Port': '1.6-609504503d68982a10f495659990084b', 'Portgroup': '1.2-37b374b19bfd25db7e86aebc364e611e', 'Conductor': '1.1-5091f249719d4a465062a1b3dc7f860d', - 'EventType': '1.0-3daeec50c6deb956990255f92b863333', + 'EventType': '1.1-5d44b591d93189b2ea91a1af9b082df6', 'NotificationPublisher': '1.0-51a09397d6c0687771fb5be9a999605d', }