Nova的通知

与其他OpenStack服务类似,Nova通过oslo.messaging提供的Notifier类向消息总线发送通知。 从通知使用者的角度来看,通知由两个部分组成 : 由oslo.messaging定义的固定结构的envelope 和 由发出通知的服务定义的有效负载。 envelope的格式如下:

{
    "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>
}

在Nova中有两种类型的通知 : 具有非版本负载的 legacy通知 和 具有版本负载的新通知。

未版本控制的通知

Nova代码使用了nova.rpc.get的通知调用来得到已被配置的oslo.messaging通知对象然后它使用oslo提供在 Notifier 对象上的函数来发出通知。 返回的Notifier对象的配置取决于 get notifier call 的参数 和 oslo.messaging 配置选项driver和topics 的值。 Nova中有 notification 配置选项,它指定特定的通知类型,如: notifications.notify_on_state_change, notifications.default_level等。

未版本控制的 notifications 的有效负载的结构是在发出通知的代码中定义的,并且不存在这种格式的文档或强制向后兼容性契约。

版本控制的通知

创建版本通知概念是为了修正非版本通知的缺点。 发出通知的 envelope 结构与oslo.messaging提供的未版本化通知案例中的 envelope 结构相同。 但是,有效负载不是一个随意的表单字典,而是一个序列化的 oslo versionedobject。

例如, service.update notification 的 wire 格式 看起来如下:

{
    "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"
}

序列化的oslo versionedobject作为一个有效负载提供了一个版本号给消费者,这样消费者就可以检测到负载的结构是否发生了变化。 Nova提供了关于版本化通知的有效负载的以下契约:

  • 如果且仅当 payload 的nova object.data 字段 的语法或语义被改变时,由 payload 的 nova object.version 字段定义的 负载版本会增加。
  • 一个小版本的bump表示一个向后兼容的变更,这意味着只有新的字段被添加到有效负载中,这样一个写得很好的消费者仍然可以在没有任何变化的情况下使用新的有效负载。
  • 一个主版本的bump指示了负载的向后不兼容的更改,这可能意味着在有效负载中删除字段、类型更改等。
  • 除了 ‘nova_object.data’ 和‘nova_object.version’,在每个负载上还有一个额外的 nova_object.name字段。

有一个Nova配置参数是 notifications.notification_format ,用于指定哪种通知可被NOVA发出。 可能的值有 unversioned, versioned,或同时设置两种值。默认情况两种都可以。

versioned notification会被发送到与 legacy notifications 不同的 topic 。默认情况下他们被发送到 versioned_notifications,但是它是可以在 nova.conf 文件里被修改 versioned_notifications_topic 配置选项的。

如何添加版本化的通知

为了从Nova代码支持上面的契约,每个版本的通知都与oslo versionedobjects建模。 每个版本的通知类都应该继承 nova.notifications.objects.base.NotificationBase,它已经定义了通知的三个强制性字段, event_type, publisher_id 和priority 。新通知类应该添加一个新的字段负载,它带有个合适的payload类型。 通知的负载对象应当继承 nova.objects.notifications.base.NotificationPayloadBase类同时应将有效负载的字段定义为versionedobject字段。 下一部分描述基类。

nova.notifications.objects.base模块

请注意,通知对象不应注册到NovaObjectRegistry,以避免将nova内部对象与通知对象混合在一起。 在每个具体的通知对象上使用register通知装饰器来替代它。

下面的代码例子为新的 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(),
    }

之后,可以使用下面的代码来填充和发送通知:

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)

上面的代码将生成以下通知 :

{
    "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>"
}

有可能通过为负载类添加一个 SCHEMA字段来重用现有的versionedobject,它定义了现有对象字段和新有效载荷对象字段之间的映射。 例如 在定义通知的有效负载时service.status通知重用现有的 nova.objects.service.Service 对象:

@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)

如果定义了 SCHEMA 字段, 那么在开始发送通知前需要通过调用 populate_schema 来填充 payload object:

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)

上面的代码会在线生成一个JSON格式的数据,格式类似于上面的event_type为myobject.update的格式。

SCHEMA 字段的数据格式遵循如下方法:

<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>)

定义在 SCHEMA 字段中的映射具有以下语义。 当 populate_schema 函数被调用时, SCHEMA字段的内容被枚举,   并且 指向参数对象的字段值被复制到请求的payload字段中。 因此,在上面的示例中, payload 对象的host字段由service对象的host字段的值填充,该值作为参数传递给populate_schema调用。

通知的有效负载对象可以重用来自多个现有对象的字段。 同样,通知可以在其有效负载中同时拥有新的和重用的字段。

注意,通知的发布者实例有两种不同的方式创建。 通过带有 host 和binary 字符串参数实例 NotificationPublisher 对象的方法创建,或者通过 NotificationPublisher.from_service_obj 方法来生成 Service 对象。

Versioned notifications 有一个示例文件存储在doc/sample_notifications 目录并且 notification对象应该用 notification_sample 装饰器来装饰。例如 service.update 通知有个示例文件存储在 doc/sample_notifications/service-update.json 并且 ServiceUpdateNotification 类相应地被装饰。

  Notification payload 类 可以使用继承 以避免在nova代码中复制常见的有效负载片段。 然而,应该在通知中创建直接用于通知的 leaf 类,以避免将来需要增加额外的继承级别,从而改变了在载荷类中出现的 leaf 类的名称。 如果不能避免这一点,唯一的变化是重命名,那么新的有效负载的版本应该与重命名之前的旧有效负载相同。

翻译自:https://docs.openstack.org/nova/latest/reference/notifications.html

猜你喜欢

转载自my.oschina.net/u/857184/blog/1630457