Nova API 微版本支持在 python-novaclient

https://blueprints.launchpad.net/python-novaclient/+spec/api-microversion-support

本规范的目的是阐明 Nova 和 python-novaclient 之间现在使用微版本所需的具体行为,并为其他客户端提供与 Nova 交互的指导。

问题描述

作为一个社区,我们在通过增量开发来演进接口和代码方面做得很好。我们在进行大规模的代码一次性发布方面做得不太好。Nova API 正在积极开发中,我们希望确保 Nova API 的使用者能够利用新功能,同时也要确保它们不会因不兼容性而损坏。

微版本通过添加一个新的 HTTP 标头来实现,具体来说就是 ‘X-OpenStack-Nova-API-Version’。Nova 接受此标头,以便客户端可以指示它希望使用的 API 版本进行通信,同样,Nova 也可以指示它正在使用的版本进行通信。

对于 Nova API,如果未提供 HTTP 标头,则使用 v2.1(稳定/kilo)版本的 API。如果 HTTP 标头中指定了无效版本,则返回 HTTP 406 Not Acceptable。如果指定了特殊的 ‘latest’ 版本,Nova 将使用其最新版本。

在对 python-novaclient[1] 进行更改以支持 Nova 的微版本时,发现缺少 Nova 和客户端在微版本不匹配或未知/未指定的微版本情况下如何交互的正式规范。

在 liberty 设计会议“Nova: Nova API v2.1 in Liberty”[2] 中讨论了本规范的必要性。

用例

为了解决 Nova 和 python-novaclient 之间的具体行为,列出了以下用例来指定预期的功能。

为了便于定义,我们将使用术语“Nova V2.0”来指代 Nova 的一个版本,该版本早于微版本并且不了解它们。同样,我们将使用术语“Nova V2.1”来指代包含对微版本支持的 Nova 版本。

对于 novaclient,我们将应用标签“旧客户端”表示不支持微版本的 novaclient,以及“新客户端”表示支持微版本的 novaclient。

用例 1:旧客户端与 Nova V2.0 通信

这与微版本引入之前的行为完全相同 - 此用例不需要客户端或服务器进行任何更改。

  • 客户端连接到 Nova,未指定 HTTP 标头 X-OpenStack-Nova-API-Version

  • Nova 不检查 X-OpenStack-Nova-API-Version 标头,并简单地将所有通信处理为 v2.0(稳定/kilo)

用例 2:旧客户端与 Nova V2.1 通信

这是 Nova 更新到支持微版本的新版本,但使用旧客户端与之通信的情况。

  • 客户端连接到 Nova,未指定 HTTP 标头 X-OpenStack-Nova-API-Version

  • Nova 未看到 X-OpenStack-Nova-API-Version HTTP 标头

  • 如果 Nova 支持 2.1 微版本,该版本等于 REST API 的 v2.0(稳定/kilo),Nova 会使用该版本的接口进行所有通信。如果删除了对微版本 2.1 的支持,Nova 将返回适当的异常,客户端应将其显示给用户。

用例 3A:新客户端与 Nova V2.0 通信(未由用户指定)

[cli 特定用例] 这是用户不请求特定微版本的新客户端尝试与旧 Nova 通信的情况。

  • 用户未在与客户端的通信中指定要使用的微版本。因此,客户端尝试使用最新的微版本。

  • 客户端连接到 Nova 并请求受支持的 API 版本。

  • Nova 不查找或解析 HTTP 标头。它只是返回包含 API 版本的 json [3]。

  • 客户端检查版本信息并选择使用 2.0(直到新客户端支持 2.1 微版本),或者通知用户它无法使用微版本与 Nova 通信并退出。

用例 3B:新客户端与 Nova V2.0 通信(用户指定)

这是用户请求特定微版本的新客户端尝试与 Nova V2.0 通信的情况。

来自 CLI

  • 用户指定对客户端有效的微版本。

  • 客户端连接到 Nova 并请求受支持的 API 版本。

  • Nova 不查找或解析 HTTP 标头。它只是返回包含 API 版本的 json [3]。

  • 客户端检查版本信息并通知用户它无法使用请求的微版本与 Nova 通信并退出。

来自 python 代码(小心)

  • 用户指定对客户端有效的微版本。

  • 客户端尝试连接到 Nova。

  • Nova 不查找或解析 HTTP 标头。它只是处理调用并返回包含结果且不包含 HTTP 标头的响应。

  • 客户端不检查标头是否缺失;请求已经处理完毕,因此没有理由这样做。

用例 3C:新客户端与 Nova V2.0 通信(向后兼容)

这是通过新客户端使用 Nova V2.0 的方法。

  • 用户将计算 api 版本指定为“2.0”。

  • [cli 特定步骤] 客户端连接到 Nova 并请求受支持的 API 版本。

  • 客户端连接到 Nova V2.0,不添加 X-OpenStack-Nova-API-Version HTTP 标头。

  • Nova 不查找或解析 HTTP 标头。它使用它所知道的唯一 API 代码路径进行通信,即 v2.0。

  • 客户端不查找或解析 HTTP 标头,它知道微版本未使用。

  • 客户端处理接收到的数据,将其显示给用户并退出。

另一种受支持的方式(仅限 CLI)

  • 用户将计算 api 版本指定为“None”。

  • 客户端使用默认主版本(目前为 2.0)。

  • 客户端应用从版本协商开始的先前用例中的步骤。

用例 4:新客户端,用户指定无效版本号

这是用户向新客户端提供无效微版本标识符(例如 ‘spam’、‘l33t’ 或 ‘1.2.3.4.5’)的情况。

  • 用户指定对客户端无效的微版本。

  • 客户端向用户返回错误,即客户端应提供

    对有效微版本标识符进行验证。

有效的微版本标识符必须符合以下正则表达式

^([1-9]d*).([1-9]d*|0|latest)$

有效微版本标识符的示例:‘2.1’、‘2.10’、‘2.latest’、‘2.0’…

用例 5:新客户端/Nova V2.1:不支持的 Nova 版本

这是新客户端请求 Nova V2.1 无法处理的版本的情况。例如,客户端支持微版本 2.1 到 2.6,而 Nova 支持版本 2.8 到 2.15。

来自 CLI

  • 用户指定计算 api 版本为“2.6”。

  • 客户端连接到 Nova 并请求受支持的 API 版本。

  • Nova 不查找或解析 HTTP 标头。它只是返回包含 API 版本的 json [3]。

  • 由于客户端不支持 Nova 支持的版本,因此它无法继续并向用户报告此情况。

来自 python 代码

  • 用户指定计算 api 版本为“2.6”。

  • 客户端连接到 Nova,提供 2.6 作为请求的微版本。

  • Nova 回复 406 Not Acceptable。

  • 由于客户端不支持 Nova 支持的版本,因此它无法继续并向用户报告此情况。

  • (另一种方法是客户端尝试使用 Nova 可接受的版本继续。请注意,在这种情况下,客户端应该能够继续,因为任何会破坏基本兼容性的更改可能需要主版本升级到 v3)

用例 6:新客户端/Nova V2.1:不支持的客户端版本

这是新客户端请求 Nova V2.1 无法处理的版本的情况。例如,客户端支持微版本 2.10 到 2.15,而 Nova 支持版本 2.1 到 2.5。

步骤与用例 5 相同。

在实践中不应发生这种情况,因为客户端应始终能够与 Nova 的任何版本通信。

用例 7:新客户端/Nova V2.1:兼容版本

这是新客户端请求 Nova V2.1 支持的版本的情况。例如,客户端支持微版本 2.8 到 2.10,而 Nova 支持版本 2.1 到 2.12。

  • [cli 特定步骤] 客户端连接到 Nova 并请求受支持的 API 版本。

  • 客户端连接到 Nova,提供 2.10 作为请求的微版本。

  • 由于 Nova 可以支持此微版本,因此它通过在 X-OpenStack-Nova-API-Version HTTP 标头中发送 2.10 来回复。

用例 8:新客户端/Nova V2.1:版本请求为‘latest’

[cli 特定用例] 这是新客户端从 Nova V2.1 请求版本为‘latest’的情况。

  • 用户指定 ‘latest’ 微版本使用。

  • 客户端连接到 Nova 并请求受支持的 API 版本。

  • Nova 不查找或解析 HTTP 标头。它只是返回包含 API 版本的 json[3]。

  • 客户端检查 API 版本信息并得出结论,当前版本支持微版本。

  • 客户端选择客户端和服务器端都支持的最新版本(通过“version”和“min_version”从 API 版本响应中获得),并连接到 Nova,在 X-OpenStack-Nova-API-Version HTTP 标头中提供所选版本

项目优先级

V2.1 API [4]

提议的变更

novaclient 中的 python 计算 API 应扩展为包含版本的主版本和次版本。它应该如下所示

  • “X.Y” - “X” 和 “Y” 接受数字值。客户端将使用它与 Nova-API 通信。

  • “X.latest” - “X” 接受数字值。客户端将使用“latest”(有关更多详细信息,请参见 latest-microversion)客户端和服务器端都支持的“X”主版本的微版本。

  • “latest” - 客户端将使用客户端已知和服务器端都支持的最新主版本和“latest”(latest-microversion)微版本。

    “X” 是主版本,“Y” 是次版本

(如果客户端无法支持该版本)应使用请求的微版本。客户端始终会在与服务器的通信中请求特定微版本。“X.latest”纯粹是来自 python 消费者的信号,表明它希望协商服务器和客户端之间的最大互支持版本。

python-novaclient 作为 CLI 工具

应使用主 API 版本指定微版本。完整的 API 版本应通过 compute-api-version 选项传输到 python-novaclient。这样可以保持向后兼容性。用户仍然可以使用仅指定主版本部分的方式。

应首先对计算 api 版本(检查格式)进行验证。正确 api 版本需要包含正确的扩展,使用正确的命令解析器等。

如果用户将 compute-api-version 指定为“None”(这意味着 –os-compute-api-version=”None”,这与未指定 compute-api-version 不同),客户端应使用默认主 API 版本,不带微版本。

帮助消息应显示所有命令、子命令及其选项以及有关受支持版本(最小值和最大值)的信息。

由于云可以有多个 Nova API 服务目录条目(v2、v2.1),因此在此处提及会很好

  • nova version-list cmd 显示所有入口点和受支持的微版本(最小值和最大值);

  • 用于发现 Nova API 入口点的默认服务类型是“compute”。要选择正确的入口点,用户应使用 ‘service-type’ cli 选项。

检查的版本应传输到 novaclient.client.Client 函数。

“latest” 微版本

“latest” 微版本是最大版本。尽管 Nova-API 接受标头中的“latest”值,但客户端不使用这种方法。客户端通过比较 API 响应和端点 URL 来发现 API 和客户端都支持的“latest”微版本,并在与 Nova-API 通信中使用它。

发现过程应如下进行

  • 客户端向 Nova API 发出一次额外的调用 - 列出所有版本[3];

  • 客户端通过比较 API 响应和端点 URL 来确定当前版本;

  • 客户端通过检查当前版本的“min_version”和“version”值来检查当前版本是否支持微版本。如果当前版本不支持微版本(“min_version”和“version”为空),则客户端使用默认主版本(2.0)。

  • 客户端选择 novaclient 和 Nova API 都支持的最新微版本。

注意

“latest”版本仅受 CLI 支持。对于使用 python-novaclient 作为库进行版本发现,请使用 novaclient.api_versions.discover_version() 方法。

默认版本

默认的微版本应更改为“latest”。此需求的目标是使 python-novaclient / Nova 通信对用户来说“即刻可用”,并且尽可能使用最新的 REST API 版本,以便用户能够利用最新的功能。

注意:此需求仅对 python-novaclient 作为 CLI 工具为真,因为 python-novaclient 作为库没有默认版本,也不应该有默认版本。

python-novaclient 作为 Python 库 (novaclient.client 入口点)

模块 novaclient.client 用作其他 Python 库中 python-novaclient 的入口点。此模块的接口不应更改以支持向后兼容性。

novaclient.client.Client 函数应接受字符串值(应检查版本格式)[向后兼容性] 或 APIVersion 对象实例作为第一个参数。

python-novaclient 应该提供一种公共方法来检查版本格式,以简化与其他库的集成。

如果指定了微版本(APIVersion 的次要部分),客户端应在每个调用中添加特殊标头 X-OpenStack-Nova-API-Version,并验证响应也包含相同的标头,这意味着 API 端支持微版本。

python-novaclient 从开发人员的角度来看:添加新的微版本

变量 novaclient.API_MIN_VERSIONnovaclient.API_MAX_VERSION 应该在每次添加新的微版本或删除旧版本时更新。

ResourceManager 的每个“版本化”方法都应使用特定的装饰器标记。该装饰器接受两个参数:起始版本和结束版本(可选)。示例

from novaclient import api_versions
from novaclient import base

class SomeResourceManager(base.Manager)
    @api_versions.wraps(min_version='2.0')
    def show(self, req, id):
        pass

    @api_versions.wraps(start_version='2.2', end_version='2.8')
    def show(self, req, id):
        pass

    @api_versions.wraps(start_version='2.9')
    def show(self, req, id):
        pass

“版本化”命令应以与 ResourceManager 的方法相同的方式使用装饰器标记。 @api_versions.wraps() 装饰器应放置在 CLI 参数装饰器之前或之后。示例

from novaclient import api_versions
from novaclient.openstack.common import cliutils

@api_versions.wraps("2.0")
@cliutils.arg("name", help="Name of the something")
@cliutils.arg("action", help="Some action")
def do_some_show(cs, args):
    pass

@cliutils.arg("name", help="Name of the something")
@cliutils.arg("action", help="Some action")
@api_versions.wraps(start_version='2.2', end_version='2.8')
def do_some_show(cs, args):
    pass

@api_versions.wraps(start_version='2.9')
def do_some_show(cs, args):
    pass

“版本化”参数应以这种方式使用

from novaclient.openstack.common import cliutils

@cliutils.arg('name', metavar='<name>', help='Name of thing.')
@cliutils.arg(
    '--some-option',
    metavar='<some_option>',
    help='Some option.',
    start_version="2.2")
@cliutils.arg(
    '--another-option',
    metavar='<another_option>',
    help='Another option.',
    start_version="2.2",
    end_version="2.9")
def do_something(cs, args):
    pass

您可以在这里找到 2.2 微版本实现的示例[5]。

备选方案

微版本的一种替代方案是不使用它们。这将导致一组大型更改同时发生,导致服务器/客户端版本不兼容。它还会导致不太频繁但更大的 API 不兼容更改。而没有人希望这样。

数据模型影响

无。此更改仅限于 API 代码。

REST API 影响

如上所述,将接受一个新的 HTTP 标头,Nova 将返回该标头。

如果客户端选择使用该标头请求特定版本,Nova 将响应,要么接受请求的版本以供未来通信使用,要么拒绝该版本请求,因为不支持该版本。

如果客户端选择不使用该标头,Nova 将假定使用的 REST API 将是 v2.1(即‘Kilo’版本中存在的 API)。REST API 目前的工作方式就是如此。

安全影响

通知影响

其他最终用户影响

希望使用自‘Kilo’版本发布以来添加到 REST API 的新功能的客户端需要开始使用此 HTTP 标头。添加新功能的事实将仅在新版本中添加,这将鼓励他们这样做。

性能影响

其他部署者影响

开发人员影响

Nova 的 REST API 的任何未来更改(无论是在请求或任何响应中)必须导致微版本更新,并在代码中进行适当的保护。

实现

负责人

主要负责人

andreykurilin - Andrey Kurilin <andr.kurilin@gmail.com>
xuhj - Alex Xu <hejie.xu@intel.com>

工作项

通过以下方式完成 python-novaclient 微版本实现
  1. https://review.openstack.org/#/c/152569 开始的补丁链

依赖项

测试

NovaClient 的功能测试应尽可能覆盖微版本。V2.2 的补丁[5]可以用作编写此类测试的方法。

文档影响

未识别到任何未被现有 API 更改流程覆盖的特定文档影响。

参考资料