diff options
Diffstat (limited to 'doc/source/reference/notifications.rst')
-rw-r--r-- | doc/source/reference/notifications.rst | 376 |
1 files changed, 8 insertions, 368 deletions
diff --git a/doc/source/reference/notifications.rst b/doc/source/reference/notifications.rst index 788b3bccde..24655345f2 100644 --- a/doc/source/reference/notifications.rst +++ b/doc/source/reference/notifications.rst @@ -1,375 +1,15 @@ -.. - 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 +================================= +Available versioned notifications +================================= - http://www.apache.org/licenses/LICENSE-2.0 +.. note:: - 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. - -Notifications in Nova -===================== - -Similarly to other OpenStack services Nova emits notifications to the message -bus with the Notifier class provided by :oslo.messaging-doc:`oslo.messaging -<reference/notifier.html>`. From the notification consumer point of view a -notification consists of two parts: an envelope with a fixed structure defined -by oslo.messaging and a payload defined by the service emitting the -notification. The envelope format is the following:: - - { - "priority": <string, selected from a predefined list by the sender>, - "event_type": <string, defined by the sender>, - "timestamp": <string, the isotime of when the notification emitted>, - "publisher_id": <string, defined by the sender>, - "message_id": <uuid, generated by oslo>, - "payload": <json serialized dict, defined by the sender> - } - -Notifications can be completely disabled by setting the following in -your nova configuration file: - -.. code-block:: ini - - [oslo_messaging_notifications] - driver = noop - -There are two types of notifications in Nova: legacy notifications which have -an unversioned payload and newer notifications which have a versioned payload. - -Unversioned notifications -------------------------- -Nova code uses the nova.rpc.get_notifier call to get a configured -oslo.messaging Notifier object and it uses the oslo provided functions on the -Notifier object to emit notifications. The configuration of the returned -Notifier object depends on the parameters of the get_notifier call and the -value of the oslo.messaging configuration options ``driver`` and ``topics``. -There are notification configuration options in Nova which are specific for -certain notification types like -:oslo.config:option:`notifications.notify_on_state_change`, -:oslo.config:option:`notifications.default_level`, etc. - -The structure of the payload of the unversioned notifications is defined in the -code that emits the notification and no documentation or enforced backward -compatibility contract exists for that format. - - -Versioned notifications ------------------------ -The versioned notification concept is created to fix the shortcomings of the -unversioned notifications. The envelope structure of the emitted notification -is the same as in the unversioned notification case as it is provided by -oslo.messaging. However the payload is not a free form dictionary but a -serialized :oslo.versionedobjects-doc:`oslo versionedobjects object <>`. - -.. _service.update: - -For example the wire format of the ``service.update`` notification looks like -the following:: - - { - "priority":"INFO", - "payload":{ - "nova_object.namespace":"nova", - "nova_object.name":"ServiceStatusPayload", - "nova_object.version":"1.0", - "nova_object.data":{ - "host":"host1", - "disabled":false, - "last_seen_up":null, - "binary":"nova-compute", - "topic":"compute", - "disabled_reason":null, - "report_count":1, - "forced_down":false, - "version":2 - } - }, - "event_type":"service.update", - "publisher_id":"nova-compute:host1" - } - -The serialized oslo versionedobject as a payload provides a version number to -the consumer so the consumer can detect if the structure of the payload is -changed. Nova provides the following contract regarding the versioned -notification payload: - -* the payload version defined by the ``nova_object.version`` field of the - payload will be increased if and only if the syntax or the semantics of the - ``nova_object.data`` field of the payload is changed. -* a minor version bump indicates a backward compatible change which means that - only new fields are added to the payload so a well written consumer can still - consume the new payload without any change. -* a major version bump indicates a backward incompatible change of the payload - which can mean removed fields, type change, etc in the payload. -* there is an additional field 'nova_object.name' for every payload besides - 'nova_object.data' and 'nova_object.version'. This field contains the name of - the nova internal representation of the payload type. Client code should not - depend on this name. - -There is a Nova configuration parameter -:oslo.config:option:`notifications.notification_format` -that can be used to specify which notifications are emitted by Nova. - -The versioned notifications are emitted to a different topic than the legacy -notifications. By default they are emitted to 'versioned_notifications' but it -is configurable in the nova.conf with the -:oslo.config:option:`notifications.versioned_notifications_topics` -config option. - -A `presentation from the Train summit`_ goes over the background and usage of -versioned notifications, and provides a demo. - -.. _presentation from the Train summit: https://www.openstack.org/videos/summits/denver-2019/nova-versioned-notifications-the-result-of-a-3-year-journey - -How to add a new versioned notification -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To support the above contract from the Nova code every versioned notification -is modeled with oslo versionedobjects. Every versioned notification class -shall inherit from the ``nova.notifications.objects.base.NotificationBase`` -which already defines three mandatory fields of the notification -``event_type``, ``publisher`` and ``priority``. The new notification class -shall add a new field ``payload`` with an appropriate payload type. The payload -object of the notifications shall inherit from the -``nova.notifications.objects.base.NotificationPayloadBase`` class and shall -define the fields of the payload as versionedobject fields. The base classes -are described in the following section. - -The nova.notifications.objects.base module -.......................................... -.. automodule:: nova.notifications.objects.base - :noindex: - :members: - :show-inheritance: - -Please note that the notification objects shall not be registered to the -NovaObjectRegistry to avoid mixing nova internal objects with the notification -objects. Instead of that use the register_notification decorator on every -concrete notification object. - -The following code example defines the necessary model classes for a new -notification ``myobject.update``:: - - @notification.notification_sample('myobject-update.json') - @object_base.NovaObjectRegistry.register.register_notification - class MyObjectNotification(notification.NotificationBase): - # Version 1.0: Initial version - VERSION = '1.0' - - fields = { - 'payload': fields.ObjectField('MyObjectUpdatePayload') - } - - - @object_base.NovaObjectRegistry.register.register_notification - class MyObjectUpdatePayload(notification.NotificationPayloadBase): - # Version 1.0: Initial version - VERSION = '1.0' - fields = { - 'some_data': fields.StringField(), - 'another_data': fields.StringField(), - } - - -After that the notification can be populated and emitted with the following -code:: - - payload = MyObjectUpdatePayload(some_data="foo", another_data="bar") - MyObjectNotification( - publisher=notification.NotificationPublisher.from_service_obj( - <nova.objects.service.Service instance that emits the notification>), - event_type=notification.EventType( - object='myobject', - action=fields.NotificationAction.UPDATE), - priority=fields.NotificationPriority.INFO, - payload=payload).emit(context) - -The above code will generate the following notification on the wire:: - - { - "priority":"INFO", - "payload":{ - "nova_object.namespace":"nova", - "nova_object.name":"MyObjectUpdatePayload", - "nova_object.version":"1.0", - "nova_object.data":{ - "some_data":"foo", - "another_data":"bar", - } - }, - "event_type":"myobject.update", - "publisher_id":"<the name of the service>:<the host where the service runs>" - } - - -There is a possibility to reuse an existing versionedobject as notification -payload by adding a ``SCHEMA`` field for the payload class that defines a -mapping between the fields of existing objects and the fields of the new -payload object. For example the service.status notification reuses the existing -``nova.objects.service.Service`` object when defines the notification's -payload:: - - @notification.notification_sample('service-update.json') - @object_base.NovaObjectRegistry.register.register_notification - class ServiceStatusNotification(notification.NotificationBase): - # Version 1.0: Initial version - VERSION = '1.0' - - fields = { - 'payload': fields.ObjectField('ServiceStatusPayload') - } - - @object_base.NovaObjectRegistry.register.register_notification - class ServiceStatusPayload(notification.NotificationPayloadBase): - SCHEMA = { - 'host': ('service', 'host'), - 'binary': ('service', 'binary'), - 'topic': ('service', 'topic'), - 'report_count': ('service', 'report_count'), - 'disabled': ('service', 'disabled'), - 'disabled_reason': ('service', 'disabled_reason'), - 'availability_zone': ('service', 'availability_zone'), - 'last_seen_up': ('service', 'last_seen_up'), - 'forced_down': ('service', 'forced_down'), - 'version': ('service', 'version') - } - # Version 1.0: Initial version - VERSION = '1.0' - fields = { - 'host': fields.StringField(nullable=True), - 'binary': fields.StringField(nullable=True), - 'topic': fields.StringField(nullable=True), - 'report_count': fields.IntegerField(), - 'disabled': fields.BooleanField(), - 'disabled_reason': fields.StringField(nullable=True), - 'availability_zone': fields.StringField(nullable=True), - 'last_seen_up': fields.DateTimeField(nullable=True), - 'forced_down': fields.BooleanField(), - 'version': fields.IntegerField(), - } - - def populate_schema(self, service): - super(ServiceStatusPayload, self).populate_schema(service=service) - -If the ``SCHEMA`` field is defined then the payload object needs to be -populated with the ``populate_schema`` call before it can be emitted:: - - payload = ServiceStatusPayload() - payload.populate_schema(service=<nova.object.service.Service object>) - ServiceStatusNotification( - publisher=notification.NotificationPublisher.from_service_obj( - <nova.object.service.Service object>), - event_type=notification.EventType( - object='service', - action=fields.NotificationAction.UPDATE), - priority=fields.NotificationPriority.INFO, - payload=payload).emit(context) - -The above code will emit the :ref:`already shown notification<service.update>` -on the wire. - -Every item in the ``SCHEMA`` has the syntax of:: - - <payload field name which needs to be filled>: - (<name of the parameter of the populate_schema call>, - <the name of a field of the parameter object>) - -The mapping defined in the ``SCHEMA`` field has the following semantics. When -the ``populate_schema`` function is called the content of the ``SCHEMA`` field -is enumerated and the value of the field of the pointed parameter object is -copied to the requested payload field. So in the above example the ``host`` -field of the payload object is populated from the value of the ``host`` field -of the ``service`` object that is passed as a parameter to the -``populate_schema`` call. - -A notification payload object can reuse fields from multiple existing -objects. Also a notification can have both new and reused fields in its -payload. - -Note that the notification's publisher instance can be created two different -ways. It can be created by instantiating the ``NotificationPublisher`` object -with a ``host`` and a ``source`` string parameter or it can be generated from a -``Service`` object by calling ``NotificationPublisher.from_service_obj`` -function. - -Versioned notifications shall have a sample file stored under -``doc/sample_notifications`` directory and the notification object shall be -decorated with the ``notification_sample`` decorator. For example the -``service.update`` notification has a sample file stored in -``doc/sample_notifications/service-update.json`` and the -ServiceUpdateNotification class is decorated accordingly. - -Notification payload classes can use inheritance to avoid duplicating common -payload fragments in nova code. However the leaf classes used directly in a -notification should be created with care to avoid future needs of adding extra -level of inheritance that changes the name of the leaf class as that name is -present in the payload class. If this cannot be avoided and the only change is -the renaming then the version of the new payload shall be the same as the old -payload was before the rename. See [1]_ as an example. If the renaming -involves any other changes on the payload (e.g. adding new fields) then the -version of the new payload shall be higher than the old payload was. See [2]_ -as an example. - -What should be in the notification payload -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is just a guideline. You should always consider the actual use case that -requires the notification. - -* Always include the identifier (e.g. uuid) of the entity that can be used to - query the whole entity over the REST API so that the consumer can get more - information about the entity. -* You should consider including those fields that are related to the event - you are sending the notification about. For example if a change of a field of - the entity triggers an update notification then you should include the field - to the payload. -* An update notification should contain information about what part of the - entity is changed. Either by filling the nova_object.changes part of the - payload (note that it is not supported by the notification framework - currently) or sending both the old state and the new state of the entity in - the payload. -* You should never include a nova internal object in the payload. Create a new - object and use the SCHEMA field to map the internal object to the - notification payload. This way the evolution of the internal object model - can be decoupled from the evolution of the notification payload. - - .. important:: This does not mean that every field from internal objects - should be mirrored in the notification payload objects. - Think about what is actually needed by a consumer before - adding it to a payload. When in doubt, if no one is requesting - specific information in notifications, then leave it out until - someone asks for it. - -* The delete notification should contain the same information as the create or - update notifications. This makes it possible for the consumer to listen only to - the delete notifications but still filter on some fields of the entity - (e.g. project_id). - -What should **NOT** be in the notification payload -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* Generally anything that contains sensitive information about the internals - of the nova deployment, for example fields that contain access credentials - to a cell database or message queue (see `bug 1823104`_). - -.. _bug 1823104: https://bugs.launchpad.net/nova/+bug/1823104 - -Existing versioned notifications -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. note:: Versioned notifications are added in each release, so the samples - represented below may not necessarily be in an older version of nova. Ensure - you are looking at the correct version of the documentation for the release - you are using. + Versioned notifications are added in each release, so the samples + represented below may not necessarily be in an older version of nova. Ensure + you are looking at the correct version of the documentation for the release + you are using. .. This is a reference anchor used in the main index page. .. _versioned_notification_samples: .. versioned_notifications:: - -.. [1] https://review.opendev.org/#/c/463001/ -.. [2] https://review.opendev.org/#/c/453077/ |