使用 UUID 在服务和 os-hypervisors API 中¶
https://blueprints.launchpad.net/nova/+spec/service-hyper-uuid-in-api
目前,我们在计算 REST API 中使用和接收主键 ID 来处理服务和超visor(计算节点)。在多单元部署中,这些 ID 不是唯一的。此规范建议在 REST API 中暴露服务和超visor的 UUID,以唯一标识资源,无论其位于哪个单元中。
问题描述¶
我们目前从计算 REST API 中泄露数据库 ID 字段(主键)用于服务和 compute_nodes,它们都位于一个单元数据库中(在 cells v2 部署中为“nova”数据库)。它们分别位于 os-services 和 os-hypervisors API 中。
例如,要删除服务记录,您必须向 /os-services/{service_id} 发送 DELETE 请求,以删除具有该 ID 的服务记录。
os-hypervisors API 在 GET(索引)请求中暴露 ID,并在“show”和“uptime”方法中使用它通过该 ID查找 ComputeNode 对象。
这在单单元部署中虽然丑陋但功能正常。但是,在多单元部署中,我们没有上下文来查询应该从哪个单元获取服务/节点详细信息,因为您可能在每个单元中都有一个 nova-compute 服务和 ID 为 1 的计算节点,那么您选择哪个单元来删除服务或显示超visor的详细信息?
用例¶
作为云管理员,我希望能够唯一标识云中的资源,无论它们位于哪个单元中,并能够获取其详细信息并删除它们。
提议的变更¶
此蓝图建议向计算 REST API 添加一个微版本,该版本将 ID 字段的使用替换为 UUID 字段。UUID 将在 GET 响应中返回,并作为 CRUD API 中 ID 的输入。
然后,当发出删除服务的请求时,如果提供了 UUID,我们可以简单地迭代单元,直到找到该服务,或者返回 404 错误。
在微版本之前,如果传递了一个 ID 并且只有一个单元,或者在多个单元中没有重复项,我们将继续响应该请求。但是,如果在请求中传递了一个 ID(在微版本之前),并且我们无法从多个单元中唯一标识记录,我们将返回 400 错误。这与创建服务器时,如果没有提供网络或端口并且有多个网络可供项目使用时,我们返回 400“NetworkAmbiguous”错误的类似行为。
compute_nodes 表已经有一个 uuid 字段。但是,services 表没有,因此作为此蓝图的一部分,我们需要向该表和相应的版本对象添加一个 uuid 列。
备选方案¶
替代方案是仅暴露基本的 UUID 并使用它来迭代潜在的多个单元,直到找到匹配项,或者将单元 UUID 编码到资源 UUID 中。例如,如果我们可以简单地返回 {cell_uuid}-{resource_uuid}。
然后,我们不必迭代所有单元来查找资源,而是可以解码输入 UUID 以获取所需的单元。
这不是一个推荐的替代方案,因为它将单元编码到 REST API 中,而我们过去说过我们不想这样做,并且类似于 cells v1 在单元上进行命名空间的方式。它还意味着计算 API 的某些部分正在编码单元 UUID,而其他部分(如 servers API)则没有。这可能会导致实际代码中的维护问题,因为我们将对不同的资源执行不同的查找操作。
另一种替代方案是在 Nova API 数据库中创建映射表,例如 host_mappings 和 instance_mappings 表。至少目前,不推荐这种替代方案,因为处理服务记录的需求应该相对较小。
数据模型影响¶
单元(nova)数据库中的 services 表将添加一个可为空的 uuid 列。由于现有记录没有 uuid 字段,因此该列将可为空。
我们可以在通过版本对象访问时迁移数据,和/或提供在线数据迁移,以便在升级期间将 UUID 添加到现有记录。
REST API 影响¶
os-hypervisors¶
此 API 中只有 GET 方法。它们都将被更改为返回 id 字段的 uuid 值,并接受 {hypervisor_id} 的 uuid 值作为输入。我们不能使用 Ocata 中添加的 查询参数验证 来验证传入的 ID 是否为 UUID,因为它不是查询参数。因此,我们需要在代码中验证输入的 id 值是否为 UUID。
以下 API 也会被更改
* GET /os-hypervisors/{hypervisor_hostname_pattern}/search
* GET /os-hypervisors/{hypervisor_hostname_pattern}/servers
这两个 API 都返回给定主机名搜索模式的匹配列表。虽然这与此规范中陈述的问题没有直接关系,但我们将利用此 API 中微版本更改的机会来改进它们。hypervisor_hostname_pattern 将更改为查询参数。
旧:GET /os-hypervisors/{hypervisor_hostname_pattern}/search
新:GET /os-hypervisors?hypervisor_hostname=xxx
示例请求
GET /os-hypervisors?hypervisor_hostname=london1.compute
示例响应
{
"hypervisors": [
{
"hypervisor_hostname": "london1.compute.1",
"id": "37c62dfd-105f-40c2-a749-0bd1c756e8ff",
"state": "up",
"status": "enabled"
}
]
}
旧:GET /os-hypervisors/{hypervisor_hostname_pattern}/servers
新:GET /os-hypervisors?hypervisor_hostname=xxx&with_servers=true
示例请求
GET /os-hypervisors?hypervisor_hostname=london1.compute&with_servers=true
示例响应
{
"hypervisors": [
{
"hypervisor_hostname": "london1.compute.1",
"id": "37c62dfd-105f-40c2-a749-0bd1c756e8ff",
"state": "up",
"status": "enabled",
"servers": [
{
"name": "test_server1",
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
},
{
"name": "test_server2",
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
}
]
}
]
}
os-services¶
以下 API 方法将接受输入和/或在响应中返回整数主键 ID,将被更新为接受/返回 UUID
* GET /os-services
* DELETE /os-services/{service_id}
例如
GET /os-services
响应
{
"services": [
{
"id": "8e6e4ab6-0662-4ff5-8994-dde92bedada1",
"binary": "nova-scheduler",
"disabled_reason": "test1",
"host": "host1",
"state": "up",
"status": "disabled",
"updated_at": "2012-10-29T13:42:02.000000",
"forced_down": false,
"zone": "internal"
},
{
"id": "3fe90b52-1d67-4f03-9ed3-5fbf1a6fa1e1",
"binary": "nova-compute",
"disabled_reason": "test2",
"host": "host1",
"state": "up",
"status": "disabled",
"updated_at": "2012-10-29T13:42:05.000000",
"forced_down": false,
"zone": "nova"
},
]
}
DELETE /os-services/3fe90b52-1d67-4f03-9ed3-5fbf1a6fa1e1
成功的删除操作没有响应。
**action** API 不接受 ID 来标识要执行操作的服务。这些包括
* PUT /os-services/disable
* PUT /os-services/disable-log-reason
* PUT /os-services/enable
* PUT /os-services/force-down
与 /servers/{server_id}/action API 将操作放在请求主体中不同,这些 API 不接受特定的服务 ID。请求主体包含一个 host 和 binary 字段来标识服务。
作为此微版本的一部分,我们将把这些 action API 合并到一个 PUT 方法中,该方法支持所有 action 并接受一个 service_id 作为输入,以唯一标识服务,而不是包含 host 和 binary 字段的主体。
以下是每个 action API 的旧格式和新格式的示例。
PUT /os-services/disable
旧请求
PUT /os-services/disable { "host": "host1", "binary": "nova-compute" }
新请求
PUT /os-services/{service_id} { "status": "disabled" }
PUT /os-services/disable-log-reason
旧请求
PUT /os-services/disable-log-reason { "host": "host1", "binary": "nova-compute", "disabled_reason": "test2" }
新请求
PUT /os-services/{service_id} { "status": "disabled", "disabled_reason": "test2" }
PUT /os-services/enable*
旧请求
PUT /os-services/enable { "host": "host1", "binary": "nova-compute" }
新请求
PUT /os-services/{service_id} { "status": "enabled" }
PUT /os-services/force-down
旧请求
PUT /os-services/force-down { "host": "host1", "binary": "nova-compute", "forced_down": true }
新请求
PUT /os-services/{service_id} { "forced_down": true }
我们还将为 PUT 方法提供完整的响应。例如
PUT /os-services/disable-log-reason
旧响应
{ "service": { "binary": "nova-compute", "disabled_reason": "test2", "host": "host1", "status": "disabled" } }
新响应
{ "service": { "id": "ade63841-f3e4-47de-840f-815322afa569", "binary": "nova-compute", "disabled_reason": "test2", "host": "host1", "state": "up", "status": "disabled", "updated_at": "2012-10-29T13:42:05.000000", "forced_down": false, "zone": "nova" } }
安全影响¶
无
通知影响¶
Services¶
service.update 版本通知有效负载将被更新,以包含新的 uuid 字段。
Hosts¶
此 API 中存在未版本化的通知,用于对计算节点执行操作,例如 HostAPI.set_enabled.start。这些尚未转换为使用版本化通知,因此在此之前,无需进行任何更改。
其他最终用户影响¶
由于 REST API 更改不会更改响应中的“id”键,只会更改值,因此无需对 python-novaclient 进行任何更改。
性能影响¶
无。由于我们在 nova_api 数据库中没有服务映射表,因此我们已经必须迭代单元来查找匹配项,如这个更改所示:https://review.openstack.org/#/c/442162/
其他部署者影响¶
一旦部署者拥有多个单元,他们可能需要更新工具以指定微版本,以唯一标识超visor 或服务,例如,删除服务。
开发人员影响¶
无
实现¶
负责人¶
- 主要负责人
Matt Riedemann (mriedem)
- 其他贡献者
Dan Peschman (dpeschman)
工作项¶
编写数据库模式迁移以添加 services.uuid 列。
- 将 uuid 字段添加到 Service 对象。
如果在创建() 时未指定,则为新服务生成 UUID。
在从数据库检索时生成并保存旧服务的 UUID,就像计算节点获得 UUID 一样 [1]。
向 ComputeNode 和 Service 对象添加 get_by_uuid 方法。
添加服务 UUID 的在线数据迁移,就像我们为计算节点所做的那样 [2]。
更新
nova.compute.api.HostAPI方法,这些方法接受一个 ID 并检查该 ID 是否为 UUID,如果是,则使用对象上的 get_by_uuid 方法查询资源,否则使用 get_by_id,就像今天一样。将微版本添加到 os-hypervisors 和 os-services API,包括验证以确保传入的 id 是 UUID。这还包括更改 os-services PUT 方法的请求格式。这可能是一个大型且相对复杂的更改进行审查,但鉴于所有这些更改都将在同一个微版本中进行,我们无法实际地将这些更改分解。
更新超visor [3] 和服务 [4] 的计算 API 响应模式验证。请注意,Tempest 响应模式已经允许整数或字符串。作为此更改的一部分,我们应该更新 Tempest 中的响应模式验证,以便在新的微版本之后,超visor 和服务 ID 应该是 UUID。
依赖项¶
无
测试¶
针对负面场景的单元测试,例如无法在多个单元中按 UUID 找到服务。我们还应该测试将非 UUID 整数值传递给带有新微版本的更改的 API,以确保查询参数验证使该请求失败并显示 400 错误。
API 示例的函数测试,以确保微版本后的响应中的“id”值是 UUID 而不是整数。
可能会添加 Tempest API 测试,尽管我们可能可以使用树内函数测试来处理相同的测试覆盖范围。
我们将使用树内函数测试来测试所有 os-services PUT 方法的更改,因为 Tempest 不会测试禁用或强制关闭计算服务,因为这会破坏并发的多租户 Tempest 运行。
文档影响¶
需要更新 os-services 和 os-hypervisors API 参考文档,以说明新的微版本接受输入并在响应中返回“id”键的 UUID 值。
参考资料¶
历史¶
发布名称 |
描述 |
|---|---|
Pike |
引入 |