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-listcmd 显示所有入口点和受支持的微版本(最小值和最大值);用于发现 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_VERSION 和 novaclient.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 微版本实现
依赖项¶
无
测试¶
NovaClient 的功能测试应尽可能覆盖微版本。V2.2 的补丁[5]可以用作编写此类测试的方法。
文档影响¶
未识别到任何未被现有 API 更改流程覆盖的特定文档影响。
参考资料¶
[0] https://specs.openstack.org/openstack/nova-specs/specs/kilo/implemented/api-microversions.html
[2] https://etherpad.openstack.org/p/YVR-nova-api-2.1-in-liberty http://libertydesignsummit.sched.org/event/60da58ea4c57a2f25b2e1ed22213d6ce#.VXA9krxZ5Qt
[3] https://github.com/openstack/nova/blob/master/doc/api_samples/versions/versions-get-resp.json
[4] https://specs.openstack.org/openstack/nova-specs/priorities/liberty-priorities.html#v2-1-api