用户消息

https://blueprints.launchpad.net/manila/+spec/user-messages

在相当长的一段时间里,OpenStack 服务一直希望能够向 API 终端用户发送消息(这里的用户不是指运维人员,而是与客户端交互的用户)。

在 Mitaka Cinder 中期会议 [6] 上,我们决定通过向 API 添加一个 /messages 资源来开始解决这个问题,以便终端用户可以查询更多关于错误的信息。Manila 也应该效仿。

问题描述

如果 Manila 操作失败,例如创建共享或创建共享组,用户将无法获得任何详细信息。只有操作状态会更新,例如错误或失败,而不会向用户提供任何更新。

用例

在以下情况下,向用户提供有关失败的异步操作的更多信息。以下是当资源(例如共享)进入错误状态时我们可以创建的原因/消息列表

  • 共享
    • 创建失败,因为找不到有效的宿主机 - 如果过滤器链没有返回任何宿主机,消息中会包含最后一个过滤器的名称。

    • 创建失败,因为驱动程序不期望在当前配置下提供共享网络。

    • 创建失败,因为未能获取共享服务器。

    • 创建失败,原因不明。

    • 访问规则删除失败。

    • 扩展错误(也通过状态表示)。

    • 收缩失败,因为配额更新失败。

    • 收缩失败,因为可能存在数据丢失。

  • 共享组
    • 创建失败,因为无法选择兼容的共享服务器。

    • 创建失败,因为共享实例未能获取共享服务器。

    • 创建失败,原因不明。

    • 创建失败,因为驱动程序不期望在当前配置下提供共享网络。

  • 共享组快照
    • 创建失败,原因不明。

    • 删除失败,原因不明。

  • 共享副本
    • 创建失败,因为必须存在处于“AVAILABLE”状态的“ACTIVE”副本才能为共享创建新副本。

    • 创建失败,因为驱动程序不期望在当前配置下提供共享网络。

    • 创建失败,因为未能获取共享副本的共享服务器。

    • 创建失败,原因不明。

    • 访问规则删除失败。

    • 共享副本更新失败,因为驱动程序错误。

  • 快照
    • 创建失败,原因不明。

    • 创建复制快照失败,原因不明。

    • 访问规则删除失败。

    • 回滚到快照失败,原因不明。

  • 快照副本
    • 驱动程序无法删除后端上部分或全部共享副本快照。

    • 更新失败,因为后端未找到副本快照。

    • 更新失败,因为驱动程序错误。

上述列表反映了共享服务中资源设置为错误状态的一部分情况 [3][4] 显示了调度错误消息的创建。

提议的变更

当异步操作期间发生错误时,以下有关错误的详细信息将存储在数据库中:* 发生错误的资源类型和 ID * 发生错误的动作 * 错误详情(用户友好的错误解释) * 错误级别 * 过期时间 * 请求 ID * 项目 ID

由于驱动程序或 Manila 服务引发的异常可能包含敏感信息(例如关于后端基础设施、主机名等),此异常不会暴露给用户,而是只暴露经过清理的错误详情。

用户可以通过新的 /messages API 或通过 Manila CLI 获取消息,Manila CLI 允许显示消息、列出所有消息和删除现有消息。此规范未涉及与 Horizon 的集成。

Cinder 中已实现相同的方法。

备选方案

  • 面向用户的通知 - 结合现有通知框架和 AMQP 消费者来提取消息并为用户提供一个端点。这种方法的缺点是,我们不希望向用户显示通知中的当前信息,并且它将需要更多的服务作为依赖项。

  • 通过独立服务(如 Zaqar)检索用户消息。此方法建议将用户消息存储在另一个服务中,用户可以查询该服务,或者该服务可以利用 Webhook 通知用户。这种方法的一个主要缺点是为独立服务编写绑定的复杂性以及需要一个独立服务作为依赖项。

此规范未解决的问题

  • 状态变更通知。此解决方案不旨在解决当共享或任何其他资源状态变更时向用户发出警报的用例。例如,当共享将其状态从 CREATING 更改为 AVAILABLE 时。

数据模型影响

数据库中新的消息表用于存储所有消息。在错误很多的云中,此表可能会变得很大。管理员将能够利用 expires_at 列通过 Manila-manage CLI 命令删除旧消息。

  • id = Column(String(36), primary_key=True, nullable=False)

  • project_id = Column(String(36), nullable=False)

  • message_level = Column(String(32), nullable=False)

  • request_id = Column(String(255), nullable=True)

  • resource_type = Column(String(255))

  • resource_uuid = Column(String(36), nullable=True)

  • action = Column(String(255), nullable=False)

  • expires_at = Column(DateTime, nullable=True)

  • detail = Column(String(255), nullable=True)

  • deleted = Column(String(36), default=’False’)

‘detail’ 列中只存储一个常量,并且将存在一个从这些常量到用户友好消息的映射(在一个文件中)

(在一个文件中) 从这些常量到用户友好消息的映射

message_map = {
MessageIds.UNEXPECTED_NETWORK: _(“当前后端配置不支持在项目定义的共享网络中创建共享。”),

“支持在项目” “定义的共享网络中创建共享。”),

MessageIds.QUOTA_UPDATE: _(“未能更新配额。”)

}

REST API 影响

消息API

  • 列出消息

    • URL: /v2/<tenant>/messages

    • 方法: GET (200, 400)

    • URL 参数

      • offset - 整数,设置偏移量以定义消息列表的起始点,0 或更大

      • limit - 整数,返回消息的最大数量,1 或更大

      • sort_key - 字符串,要排序的键(例如 ‘created_at’ 或 ‘resource_type’)

      • sort_dir - 字符串,排序方向,应为 ‘asc’ 或 ‘desc’。

    • JSON body

      {
        "messages": [
          {
           "id": "5429fffa-5c76-4d68-a671-37a8e24f37cf",
           "action": "ALLOCATE_HOST",
           "user_message": "No storage could be allocated for this share
                           request. Trying again with a different size or
                           share type may succeed.",
           "message_level": "ERROR",
           "resource_type": "SHARE",
           "resource_uuid": "f292cc0c-54a7-4b3b-8174-d2ff82d87008",
           "created_at": 2015-08-27T09:49:58-05:00,
           "expires_at": 2015-09-27T09:49:58-05:00,
           "request_id": "req-936666d2-4c8f-4e41-9ac9-237b43f8b848",
          }
        ]
      }
      
  • 显示消息

    • URL: /v2/<tenant>/messages/<message_id>

    • 方法: GET (200, 403 Forbidden, 404 Not Found)

    • JSON body

      {
        "message": {
          "id": "5429fffa-5c76-4d68-a671-37a8e24f37cf",
          "action": "ALLOCATE_HOST",
          "user_message": "No storage could be allocated for this share
                          request. Trying again with a different size or
                          share type may succeed.",
          "message_level": "ERROR",
          "resource_type": "SHARE",
          "resource_uuid": "f292cc0c-54a7-4b3b-8174-d2ff82d87008",
          "created_at": 2015-08-27T09:49:58-05:00,
          "expires_at": 2015-09-27T09:49:58-05:00,
          "request_id": "req-936666d2-4c8f-4e41-9ac9-237b43f8b848",
        }
      }
      
  • 删除消息

    • URL: /v2/<tenant>/messages/<message_id>

    • 方法: DELETE (204, 403 Forbidden, 404 Not Found)

驱动程序影响

安全影响

数据库中只保留消息类型(而不是内部错误消息)并暴露给用户,以确保不会暴露敏感信息。例如,后端驱动程序可能会暴露敏感信息或基础设施主机名。

通知影响

其他最终用户影响

用户将能够通过新的 API 或 CLI 命令列出、显示和删除用户消息。

性能影响

其他部署者影响

  • 新的配置选项 message_ttl 将决定在消息创建时间之后多少秒设置生成消息的 ‘expires_at’ 属性。

  • 消息表可能会很大,并且可能会根据 ‘expires_at’ 列进行回收。所有 expires_at 日期早于当前时间的消息都可以安全删除。

开发人员影响

开发人员应注意用户需要错误信息的用例。在这种情况下,应编写适当的用户消息,并将消息的创建添加到特定的代码路径中。

实现

负责人

主要负责人

Jan Provaznik Alex Meade

Alex Meade 是消息方法的作者,本规范主要基于 Alex 现有补丁(参见参考资料)。

工作项

  • 添加新的数据库表和用于存储消息的迁移。

  • 添加 /messages API [1]

  • 通过消息命令扩展 CLI [2]

  • 使用用于列出、显示和删除消息的接口扩展 Horizon UI。

  • 在调度器和共享服务中的适当位置添加“message create”调用 [3][4]

  • 添加新的“manila-manage message delete-expired”命令,用于从数据库中删除过期消息。

依赖项

测试

应编写 Tempest 测试并在闸门中运行。实现功能的完整功能测试可能很困难,因为只有发生错误时才会创建消息,这可能难以触发。但是,通过无限配额,某些操作很容易触发失败。一个例子是创建一个过大的共享,无法存储在后端。

文档影响

  • REST API 文档。

  • 新配置选项,message_ttl (生存时间)。

  • 消息的新 API 策略。

参考资料