ironic/doc/source/dev/notifications.rst
Yuriy Zveryanskyy d5e24cf6f8 Trivial fix of notifications doc
Fix "properties" field in the examples and add small note.

Change-Id: Id63e8d95fc7f2ba1cf956eff657b3a2d902d9f93
2016-10-19 11:11:47 +03:00

318 lines
13 KiB
ReStructuredText

.. _notifications:
=============
Notifications
=============
Ironic notifications are events intended for consumption by external services
like a billing or usage system, a monitoring data store, or other OpenStack
services. Notifications are sent to these services over a message bus by
oslo.messaging's Notifier class [1]_. The consumer sees the notification as a
JSON object structured in the following way as defined by oslo.messaging::
{
"priority": <string, defined 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>
}
Versioned notifications in ironic
=================================
To make it easier for consumers of ironic's notifications to use predictably,
ironic defines each notification and its payload as oslo versioned objects
[2]_.
An increase in the minor version of the payload will indicate that only
new fields have been added since the last version, so the consumer can still
use the notification as it did previously. An increase in the major version of
the payload indicates that the consumer can no longer parse the notification as
it did previously, indicating that a field was removed or the type of the
payload field changed.
Ironic exposes a configuration option in the ``DEFAULT`` section called
``notification_level`` that indicates the minimum level for which
notifications will be emitted. This option is not defined by default, which
indicates that no notifications will be sent by ironic. Notification levels
may be "debug", "info", "warning", "error", or "critical", and each
level follows the OpenStack logging guidelines [3]_. If it's desired that
ironic emit all notifications, the config option should be set to "debug", for
example. If only "warning", "error", and "critical" notifications are needed,
the config option should be set to "warning". This level gets exposed in the
notification on the wire as the "priority" field.
All ironic versioned notifications will be sent on the message bus via the
``ironic_versioned_notifications`` topic.
Ironic also has a set of base classes that assist in clearly defining the
notification itself, the payload, and the other fields not auto-generated by
oslo (level, event_type and publisher_id). Below describes how to use these
base classes to add a new notification to ironic.
Adding a new notification to ironic
===================================
To add a new notification to ironic, a new versioned notification class should
be created by subclassing the NotificationBase class to define the notification
itself and the NotificationPayloadBase class to define which fields the new
notification will contain inside its payload. You may also define a schema to
allow the payload to be automatically populated by the fields of an ironic
object. Here's an example::
# The ironic object whose fields you want to use in your schema
@base.IronicObjectRegistry.register
class ExampleObject(base.IronicObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.IntegerField(),
'uuid': fields.UUIDField(),
'a_useful_field': fields.StringField(),
'not_useful_field': fields.StringField()
}
# A class for your new notification
@base.IronicObjectRegistry.register
class ExampleNotification(notification.NotificationBase):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'payload': fields.ObjectField('ExampleNotifPayload')
}
# A class for your notification's payload
@base.IronicObjectRegistry.register
class ExampleNotifPayload(notification.NotificationPayloadBase):
# Schemas are optional. They just allow you to reuse other objects'
# fields by passing in that object and calling populate_schema with
# a kwarg set to the other object.
SCHEMA = {
'a_useful_field': ('example_obj', 'a_useful_field')
}
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'a_useful_field': fields.StringField(),
'an_extra_field': fields.StringField(nullable=True)
}
SCHEMA defines how to populate the payload fields. It's an optional
attribute that subclasses may use to easily populate notifications with
data from other objects.
It is a dictionary where every key value pair has the following format::
<payload_field_name>: (<data_source_name>,
<field_of_the_data_source>)
The ``<payload_field_name>`` is the name where the data will be stored in the
payload object; this field has to be defined as a field of the payload.
The ``<data_source_name>`` shall refer to name of the parameter passed as
kwarg to the payload's ``populate_schema()`` call and this object will be
used as the source of the data. The ``<field_of_the_data_source>`` shall be
a valid field of the passed argument.
The SCHEMA needs to be applied with the ``populate_schema()`` call before the
notification can be emitted.
The value of the ``payload.<payload_field_name>`` field will be set by the
``<data_source_name>.<field_of_the_data_source>`` field. The
``<data_source_name>`` will not be part of the payload object internal or
external representation.
Payload fields that are not set by the SCHEMA can be filled in the same
way as in any versioned object.
Then, to create a payload, you would do something like the following. Note
that if you choose to define a schema in the SCHEMA class variable, you must
populate the schema by calling ``populate_schema(example_obj=my_example_obj)``
before emitting the notification is allowed::
my_example_obj = ExampleObject(id=1,
a_useful_field='important',
not_useful_field='blah')
# an_extra_field is optional since it's not a part of the SCHEMA and is a
# nullable field in the class fields
my_notify_payload = ExampleNotifyPayload(an_extra_field='hello')
# populate the schema with the ExampleObject fields
my_notify_payload.populate_schema(example_obj=my_example_obj)
You then create the notification with the oslo required fields (event_type,
publisher_id, and level, all sender fields needed by oslo that are defined
in the ironic notification base classes) and emit it::
notify = ExampleNotification(
event_type=notification.EventType(object='example_obj',
action='do_something', status=fields.NotificationStatus.START),
publisher=notification.NotificationPublisher(
service='ironic-conductor',
host='hostname01'),
level=fields.NotificationLevel.DEBUG,
payload=my_notify_payload)
notify.emit(context)
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", "error", 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. "error" 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.<object>.<action>.<status>`` on the message bus.
This example will send the following notification over the message bus::
{
"priority": "debug",
"payload":{
"ironic_object.namespace":"ironic",
"ironic_object.name":"ExampleNotifyPayload",
"ironic_object.version":"1.0",
"ironic_object.data":{
"a_useful_field":"important",
"an_extra_field":"hello"
}
},
"event_type":"baremetal.example_obj.do_something.start",
"publisher_id":"ironic-conductor.hostname01"
}
Available notifications
=======================
.. TODO(mariojv) Move the below to deployer documentation.
.. TODO(mariojv) Match Nova's tabular formatting below.
The notifications that ironic emits are described here. They are listed
(alphabetically) by service first, then by event_type. All examples below
show payloads before serialization to JSON.
------------------------------
ironic-conductor notifications
------------------------------
baremetal.node.power_set
------------------------
* ``baremetal.node.power_set.start`` is emitted by the ironic-conductor service
when it begins a power state change. It has notification level INFO.
* ``baremetal.node.power_set.end`` is emitted when ironic-conductor
successfully completes a power state change task. It has notification level
INFO.
* ``baremetal.node.power_set.error`` is emitted by ironic-conductor when it
fails to set a node's power state. It has notification level ERROR. This can
occur when ironic fails to retrieve the old power state prior to setting the
new one on the node, or when it fails to set the power state if a change is
requested.
Here is an example payload for a notification with this event type. The
"to_power" payload field indicates the power state to which the
ironic-conductor is attempting to change the node::
{
"priority": "info",
"payload":{
"ironic_object.namespace":"ironic",
"ironic_object.name":"NodeSetPowerStatePayload",
"ironic_object.version":"1.0",
"ironic_object.data":{
"clean_step": None,
"console_enabled": False,
"created_at": "2016-01-26T20:41:03+00:00",
"driver": "fake",
"extra": {},
"inspection_finished_at": None,
"inspection_started_at": None,
"instance_uuid": "d6ea00c1-1f94-4e95-90b3-3462d7031678",
"last_error": None,
"maintenance": False,
"maintenance_reason": None,
"network_interface": "flat",
"name": None,
"power_state": "power off",
"properties": {
"memory_mb": 4096,
"cpu_arch": "x86_64",
"local_gb": 10,
"cpus": 8},
"provision_state": "available",
"provision_updated_at": "2016-01-27T20:41:03+00:00",
"resource_class": None,
"target_power_state": None,
"target_provision_state": None,
"updated_at": "2016-01-27T20:41:03+00:00",
"uuid": "1be26c0b-03f2-4d2e-ae87-c02d7f33c123",
"to_power": "power on"
}
},
"event_type":"baremetal.node.power_set.start",
"publisher_id":"ironic-conductor.hostname01"
}
baremetal.node.power_state_corrected
------------------------------------
* ``baremetal.node.power_state_corrected.success`` is emitted by
ironic-conductor when the power state on the baremetal hardware is different
from the previous known power state of the node and the database is corrected
to reflect this new power state. It has notification level INFO.
Here is an example payload for a notification with this event_type. The
"from_power" payload field indicates the previous power state on the node,
prior to the correction::
{
"priority": "info",
"payload":{
"ironic_object.namespace":"ironic",
"ironic_object.name":"NodeCorrectedPowerStatePayload",
"ironic_object.version":"1.0",
"ironic_object.data":{
"clean_step": None,
"console_enabled": False,
"created_at": "2016-01-26T20:41:03+00:00",
"driver": "fake",
"extra": {},
"inspection_finished_at": None,
"inspection_started_at": None,
"instance_uuid": "d6ea00c1-1f94-4e95-90b3-3462d7031678",
"last_error": None,
"maintenance": False,
"maintenance_reason": None,
"network_interface": "flat",
"name": None,
"power_state": "power off",
"properties": {
"memory_mb": 4096,
"cpu_arch": "x86_64",
"local_gb": 10,
"cpus": 8},
"provision_state": "available",
"provision_updated_at": "2016-01-27T20:41:03+00:00",
"resource_class": None,
"target_power_state": None,
"target_provision_state": None,
"updated_at": "2016-01-27T20:41:03+00:00",
"uuid": "1be26c0b-03f2-4d2e-ae87-c02d7f33c123",
"from_power": "power on"
}
},
"event_type":"baremetal.node.power_state_corrected.success",
"publisher_id":"ironic-conductor.cond-hostname02"
}
.. [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