添加扩展卷完成操作

https://blueprints.launchpad.net/cinder/+spec/extend-volume-completion-action

该蓝图提出一个新的卷操作,Nova 可以使用该操作在处理 volume-extended 外部服务器事件时通知 Cinder 成功或失败。新的卷操作用于为 NFS、NetApp NFS、Powerstore NFS 和 Quobyte 卷驱动程序添加支持扩展已挂载卷的功能。

问题描述

Cinder 中的许多基于 remotefs 的卷驱动程序使用 qemu-img resize 命令来扩展卷文件。但是,当卷附加到客户机时,QEMU 将锁定该文件,并且 qemu-img 将无法调整其大小。

在这种情况下,只有持有锁定的 QEMU 进程才能调整卷的大小,这可以通过 QEMU 监视器命令 block-resize 触发。

目前,Cinder 没有足够的方法来使用此功能,因此 NFS、NetApp NFS、Powerstore NFS 和 Quobyte 卷驱动程序都禁用了扩展已挂载卷的功能。

用例

作为用户,我希望在卷附加到实例时扩展 NFS/NetApp NFS/Powerstore NFS/Quobyte 卷,并且希望卷的大小和状态反映操作的成功或失败。

提议的变更

Nova 的 libvirt 驱动程序在处理 volume-extended 外部服务器事件时使用 block-resize 命令,以告知 QEMU 附加卷的大小已更改。原则上,它也可以扩展卷文件,但目前无法向 Cinder 提供操作成功的反馈。

目前,Cinder 仅在完成扩展操作并重置卷状态从 extending 回到 in-use 后,才会向 Nova 发送 volume-extended 外部服务器事件。

该规范建议为卷驱动程序提供一种机制,使其可以延迟完成扩展操作,直到发送了 volume-extended 事件并且 Cinder 收到 Nova 的成功处理反馈为止。

该规范还提出了一种新的卷操作,Nova 将使用该操作向 Cinder 提供此反馈。

API

引入一个新的 API 微版本,添加新的 os-extend_volume_completion 卷操作。

该卷操作接受一个布尔值 error 参数,指示扩展附加卷的成功或失败。它旨在仅由 Nova 用于通知 Cinder,并将添加适当的策略来强制执行此操作。

API.extend_volume_completion

新的卷操作将由卷 API 中的新方法处理

def extend_volume_completion(self,
                             context: context.RequestContext,
                             volume: objects.Volume,
                             error: bool) -> None:

该新方法期望卷的状态为 extending,并且在其管理元数据中具有 extend_reservationsextend_new_size 键。第一个应该包含配额预留列表,第二个应该包含一个大于卷当前大小的整数,表示扩展后的新大小。

如果未满足这些条件,则将引发 InvalidVolume 异常,导致 HTTP 响应为 400 Bad Request

如果满足条件,它将从管理元数据中删除大小和预留,并通过 RPC 调用 VolumeManager.extend_volume_completion(),将两者都作为参数传递。

VolumeManager.extend_volume_completion

def extend_volume_completion(self,
                             context: context.RequestContext,
                             volume: objects.Volume,
                             new_size: int,
                             reservations: list[str],
                             error: bool) -> None:

此方法行为很大程度上取决于 error 参数

  • 如果 errorTrue,该方法将回滚配额预留,将卷状态设置为 error_extending,并记录错误。

  • 如果 errorFalse,它将完成配额预留,将卷的大小字段更新为新大小,并将卷状态重置为 availablein-use,具体取决于是否存在附件。它还将更新池统计信息并发送带有新卷大小的 resize.end 通知。

这与 VolumeManager.extend_volume() 当前处理卷驱动程序的 extend_volume() 方法的成功和失败的方式相同,只是此方法不会使用 volume-extended 外部服务器事件通知 Nova。

VolumeDriver.extend_volume

将引入一种机制,驱动程序的 extend_volume() 方法可以以此为信号通知卷管理器,它必须等待来自 Nova 的响应才能完成扩展操作。这可以采用返回值或卷管理器必须捕获的新异常的形式。

NFS、NetApp NFS、Powerstore NFS 和 Quobyte 卷驱动程序当前在其各自的 extend_volume 方法中具有检查,如果将要调整大小的卷已附加,则会引发异常,导致操作失败。这些检查将被删除。

相反,驱动程序将捕获由于卷文件被锁定而导致的任何异常(有关如何操作的示例,请参阅 [1] 中提出的对 nfs.py 的更改),并通知卷管理器需要来自 Nova 的反馈。

VolumeManager.extend_volume

对卷驱动程序的 extend_volume() 方法的调用将按如下方式处理

  • 如果调用失败,将使用 error=True 调用 extend_volume_completion

  • 如果调用成功,但卷未附加,将使用 error=False 调用 extend_volume_completion

  • 如果调用成功,并且卷已附加,将使用 error=False 调用 extend_volume_completion,并且将使用外部服务器事件通知 Nova。

这与该方法的当前内联行为相匹配,并涵盖所有驱动程序的离线扩展,以及先前支持它的驱动程序的在线扩展。

为了支持必须依赖 Nova 进行在线扩展的基于 remotefs 的驱动程序,将处理两个额外的案例

  • 如果驱动程序通知卷管理器需要来自 Nova 的响应,但卷未附加,或者卷附加到多个实例,则将处理为失败,并使用 error=True 调用 extend_volume_completion

    由于共享卷文件被锁定为只读,因此目前不值得为此功能添加多附件支持。但是,如果其他驱动程序需要它,将来可能会添加支持,例如通过允许 Cinder 处理相同卷的多个完成操作。

  • 如果驱动程序通知卷管理器需要来自 Nova 的响应,并且卷已附加到恰好一个实例,则 Cinder 将使用键 extend_reservationsextend_new_size 将配额预留和目标大小存储在管理元数据中。

    然后,它将尝试使用 [4] 中提出的新的 Nova API 微版本发送 volume-extended 外部服务器事件,确保 Nova 支持使用 os-extend_volume_completion 操作。

    • 如果已成功将 volume-extended 事件提交给 Nova,则此方法将正常返回。现在,卷将保留在 extending 状态,这将向 Nova 发出信号,表明它应该使用 Nova 子部分中描述的 os-extend_volume_completion 操作进行响应。

    • 如果无法提交 volume-extended 事件,则将通过调用 extend_volume_completion 并使用 error=True 来回滚操作。

      如果 Nova 不支持所需的微版本,或者外部事件 API 响应了诸如 403404 之类的错误代码,则可能会发生这种情况。

可见的管理元数据

必须将 extend_new_size 存储在管理元数据中,因为常规卷元数据可由用户编辑。恶意用户否则可能会在操作期间编辑目标大小以绕过其配额。

Cinder 支持将选定的键映射到常规元数据,从而覆盖任何用户设置的相同键的值。管理元数据对客户端不可见。

将在 cinder/api/api_utils.py 中将键 extend_new_size 添加到可见的管理元数据列表中,以便 Nova 能够读取扩展操作的目标大小。

OpenStack SDK

将向 OpenStack SDK 添加对新卷操作的支持,Nova 将使用该支持来调用它。

Nova

当 Nova API 收到 volume-extended 外部服务器事件时,并且调用使用了 [4] 中提出的新微版本,它将检查目标计算服务版本。如果目标计算代理太旧而无法支持该功能,则 API 将丢弃该事件并使用 "error": true 调用 os-extend_volume_completion 卷操作。

否则,该事件将转发到计算代理。在处理 volume-extended 外部服务器事件时,计算将检查卷状态

  • 如果卷状态为 extending,则计算将尝试从卷的元数据中读取 extend_new_size,并使用此值作为卷的新大小,而不是卷大小字段。

    在成功扩展卷后,它将使用 "error": false 调用卷的扩展卷完成操作。

    如果出现任何问题,包括 extend_new_size 缺失于元数据中,或者小于卷的当前大小,计算将记录错误并使用 "error": true 调用扩展卷完成操作。

  • 对于任何其他卷状态,该事件的处理方式与以前相同。

Nova 中所做的更改在 Nova 规范的当前版本中详述于 [4]

os-reset_status

在从状态 extending 重置时,os-reset_status 卷操作将检查管理元数据中的 extend_reservations 键。如果它找到配额预留键,它将尝试回滚它们。

这样做是为了避免在 Cinder 和 Nova 之间的通信丢失并且必须重置状态以重试调整大小的情况下,配额预留堆积。

然后,将从管理元数据中删除键 extend_reservationsextend_new_size

备选方案

  • 之前的更改尝试使用 volume-extended 外部服务器事件来支持 NFS 驱动程序的在线扩展 [1],但完全不依赖于从 Nova 到 Cinder 的反馈。相反,它将设置卷的新大小,将状态更改回 in-use,通知 Nova,并希望一切顺利。

    如果 Nova 侧出现任何问题,这仍然会导致卷状态指示操作已成功,这是不可接受的。

  • [2][3] 中提出的规范提出了一种新的同步 API,Nova 可以使用该 API 来触发辅助调整大小操作。此 API 将提供一个单一机制来触发调整大小操作,将新大小传达给 Nova,并获取操作成功的反馈。

    同步 API 的问题是,RPC 和 API 超时限制了扩展操作可以花费的最大时间。对于 QEMU,这似乎是可以接受的,因为为 block-resize 命令禁用存储预分配,并且因为所有合理的的文件系统都支持稀疏文件操作。

    但是,正如 [2] 中的评论员指出的那样,这可能不适用于将来可能需要此 API 的其他卷或 virt 驱动程序。它还将破坏 Nova 和 Cinder 之间建立的异步协调模式,其中包括辅助快照和卷迁移功能。

  • 遵循此模式,我们可以使提出的 API 异步,并使用类似于 Nova 的 os-assisted-volume-snapshots API 的新回调,该 API 使用 os-update_snapshot_status 快照操作向 Cinder 提供反馈。

    新的 Nova API 的功能将只是触发操作并通信新的大小。问题在于,是否值得为 Nova 添加一个新的 API,因为已经存在可以用于这两者的机制。

  • 在 Nova 中触发扩展操作的现有机制当然是 volume-extended 外部服务器事件。正如本规范所建议的那样,将其用于此目的,需要单独传输目标大小,因为外部服务器事件只有一个可自由使用的文本字段,对于 volume-extended 而言,该字段已经被用于卷 ID。

    除了像本规范建议的那样将其存储在管理员元数据中,还可以选择更新卷的大小字段,就像 [1] 本质上所做的那样。

    如果 Nova 的错误响应丢失,卷将继续保持新的大小。我们需要扩展 os-reset_status 以允许大小重置,或者类似的东西来清理像这样的卷。这是可行的,但仅在卷成功扩展后才更新大小字段似乎是一个更干净的解决方案。

  • 我们还可以扩展外部服务器事件 API 以接受事件的附加数据,并使用它将新的大小通信给 Nova。

    审查员在先前版本的本规范中对这个选项表示赞同 [2],但它将是对 Nova API 的更复杂更改。

    但是,如果外部服务器事件 API 的未来版本中可用额外的字段,那么使用这些字段而不是卷元数据将是一个相对较小的更改。

数据模型影响

REST API 影响

从新的微版本开始,POST /v3/{project_id}/volumes/{volume_id}/action API 将接受以下形式的请求体

{
    "os-extend_volume_completion": {
        "error": false
    }
}

其中 error 指示调整大小操作的成功或失败。

如果卷不存在,则返回码将是 404 Not Found

如果卷状态和管理员元数据没有指示 Cinder 正在等待扩展卷完成操作,则返回码将是 400 Bad Request

否则,返回码将是 202 Accepted

新的卷操作仅供 Nova 使用,并且需要调用者具有管理员权限。

安全影响

Active/Active HA 影响

通知影响

其他最终用户影响

性能影响

其他部署者影响

开发人员影响

实现

负责人

主要负责人

kgube

工作项

  • 将扩展完成代码从 VolumeManager.extend_volume 移动到新方法并添加测试。

  • 创建新的卷操作并添加单元测试。

  • 为新的 os-extend_volume_completion 操作添加一个新的微版本。

  • 添加 OpenStack SDK 支持。

  • 添加 Nova 支持。

  • 更新驱动程序以使用此功能。

  • 调整 devstack-plugin-nfs-tempest CI-jobs 以同时测试在线卷扩展。

依赖项

  • Nova 对回调的支持 [4]

测试

  • 卷操作的单元测试将测试所有可能的 API 响应条件。

  • VolumeManager.extend_volume 的单元测试将测试 VolumeManager.extend_volume 中描述的所有代码路径。

  • 新的卷操作不能被 Tempest 独立测试,因为它需要卷处于外部无法重现的状态。但是,当使用使用此功能的卷驱动程序之一运行它们时,它涵盖了现有的在线卷扩展测试。作为 Cinder 和 Nova CI 门控的一部分运行的 devstack-plugin-nfs-tempest jobs 将被配置为启用在线卷扩展测试。

文档影响

块存储 API 参考将被更新以包含新的卷操作。

卷驱动程序支持矩阵将被更新以显示受影响驱动程序的在线调整大小支持。

参考资料