Fixes the documentation to indicate that the notification
event types can have status 'error'. 'fail' is incorrect.
(11fcf6b710/ironic/objects/notification.py (L48)
)
Change-Id: I13c3f40430a4d7f8c07fdd9861b2d8712b8247a6
8.3 KiB
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 class1. 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
guidelines3. 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, new versioned notification classes 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='start'),
publisher=notification.NotificationPublisher(service='conductor',
host='cond-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":"conductor.cond-hostname01"
}
Existing notifications
Descriptions of notifications emitted by ironic will be documented here when they are added.