添加社区级别镜像共享

此规范主要基于 Iccha Sethi 之前创建的一个蓝图,后来由 Louis Taylor 采纳。我们 Symantec 已经在本地环境中实现了类似的功能。我们希望将此功能重新定位到 Newton 版本。

蓝图可在以下网址找到:https://blueprints.launchpad.net/glance/+spec/community-level-v2-image-sharing

此功能将允许镜像所有者在多个租户/项目之间共享镜像,而无需通过 glance API V2 显式创建成员。 “社区镜像”不会出现在用户的默认镜像列表中。

此新功能为底层数据模型中的镜像可见性添加了一个新的值,命名为 community

问题描述

目前,除非镜像是公开的,或者镜像所有者显式地与其它用户共享,否则镜像无法供除镜像所有者以外的用户使用。 如果需要访问镜像的用户数量很多,显式共享的开销可能会给镜像所有者带来负担。 此外,如果这些镜像对大多数用户都不需要,那么如果全部公开,镜像列表结果将会被垃圾信息充斥。

公开镜像出现在所有用户的 image-list 中。 这可能是不希望看到的,因为

  1. 一个开源项目希望提供一个准备好的虚拟机镜像,以便用户只需从该镜像启动实例即可使用该项目的软件。 该项目的开发人员正忙于编写软件,无暇维护“客户”列表,因为他们需要使用当前的 v2 镜像共享功能来做这件事。 与此同时,托管准备好的镜像的云提供商不希望将此镜像公开,因为这会暗示镜像消费者与云提供商之间不存在支持关系。 此外,如果发布了太多的此类镜像,那么镜像列表将会被垃圾信息充斥,因为大多数用户不应该看到这种类型的镜像。

  2. 一个云提供商希望阻止用户使用过时的公共镜像。 例如,缺少某些安全补丁或不再受供应商支持的镜像。 云提供商不希望删除该镜像,因为仍有一些用户需要它。 用户可能需要旧镜像来重建服务器,或者因为他们对该特定镜像有自定义补丁。

在上述两种情况下,供应商可以将镜像设为“社区”镜像,从而缓解上述挑战。 这意味着

a) 社区镜像不会出现在用户的默认镜像列表中。 这意味着除非他们有动力去寻找它,例如向其他用户询问 UUID,否则他们不会知道它的存在。

b) 由于该镜像不再“公开”,因此它不会暗示与提供商提供的公共镜像相同的支持级别。

提议的变更

将在 JSON 模式的 visibility 枚举中添加一个附加值,命名为 'community'。 这使得 visibility 的可能值

['public', 'private', 'shared', 'community']

具有这些 visibility 值的镜像具有以下属性

  • public:

    • 谁:所有用户

      • 在默认的 image-list 中拥有此镜像

      • 可以查看此镜像的 image-detail

      • 可以从此镜像启动

  • private:

    • 谁:具有 tenant_id == owner_tenant_id 的用户

      • 在默认的 image-list 中拥有此镜像

      • 查看此镜像的 image-detail

      • 可以从此镜像启动

  • 共享:

    • 谁:具有 tenant_id == owner_tenant_id

      • 在默认的 image-list 中拥有此镜像

      • 可以查看此镜像的 image-detail

      • 可以从此镜像启动

      • 可以查看此镜像的完整成员列表

    • 谁:在 member-list 中具有 tenant_idmember_status == 'accepted' 的用户

      • 在默认的 image-list 中拥有此镜像

      • 可以查看此镜像的 image-detail

      • 可以从此镜像启动

      • 可以查看仅包含他们自己的成员列表

    • 谁:在 member-list 中具有 tenantIdmember_status == 'pending'member_status == 'rejected' 的用户

      • 在他们的默认 image-list 中没有此镜像

      • 可以查看此镜像的 image-detail

      • 可以从此镜像启动

      • 可以查看仅包含他们自己的成员列表

  • community:

    • 谁:具有 tenant_id == owner_tenant_id

      • 在默认的 image-list 中拥有此镜像

      • 可以查看此镜像的 image-detail

      • 可以从此镜像启动

    • 谁:所有用户

      • 在他们的默认 image-list 中没有此镜像

      • 可以查看此镜像的 image-detail

      • 可以从此镜像启动

成员行为和转换

经过大量讨论,我们决定此主题足够复杂,可以分解为单独的提案。 鉴于大量的状态转换以及几位人士提出的问题用例,完全设计“正确”的行为超出了此规范的范围。

社区镜像的初始实现行为将尊重 glance 中的当前范例——更改可见性不会影响镜像的内部成员列表。 确切地说,与当前行为一致

  • 即使镜像的可见性更改为使成员列表失效的值,镜像的成员列表仍然存在。

  • 仅当镜像具有成员列表未失效的可见性时,才允许镜像成员操作(即,创建镜像成员或更改镜像成员的 member_status)。 这样做的理由是,当这些操作对镜像的可访问性没有影响时,允许用户对镜像执行成员操作会令人困惑。

备选方案

添加镜像别名

解决云提供商用例(阻止用户使用过时的公共镜像)的完全不同的方法是创建一个镜像别名机制,该机制可以指向公共镜像的最新版本。 存在一个废弃的此功能的蓝图 [1]。 但是,这更难实现,并且不符合其它用例。

添加镜像共享的特殊情况

实现此功能的另一种方法是为目标为 "community"(即,与所有租户共享)且 membership_status = "community" 的镜像添加成员记录。 这非常简单地将其标记为社区镜像,并且只需要对现有代码进行少量修改。

这符合 glance v2 API 中的当前反垃圾邮件规定。 当镜像所有者将镜像设为“社区”镜像时,任何其他租户都应该能够从该镜像启动实例。 该镜像不会出现在任何租户的默认镜像列表中。

这种方法可能会导致一些角落情况,从而导致令人惊讶的 API 调用以及数据模型级别和 API 级别可见性值之间不太理想的映射。

数据模型影响

模式更改

镜像的可见性将存储在 images 表中的数据库中的新列中,命名为 visibility。 可见性将在 ['public', 'private', 'shared', 'community'] 集合中。

visibility 的默认值为 shared。(请参阅 其他最终用户影响 以了解讨论。)

此更改使 is_public 列变得多余。 如果没有 v1 代码实际使用 is_public,则将删除该列。

将添加适当的索引以促进快速响应。

数据库迁移

(注意:这是一个预期结果的声明,而不是执行迁移的算法。)

  1. 所有 is_public == 1 的行

    • visibility = public

  2. 对于 image_members 中所有唯一的 image_id,其中 deleted != 1 且该镜像的 is_public == 0

    • visibility = shared

  3. 对于所有其他镜像

    • visibility = private

    (请参阅 其他最终用户影响 以了解进一步讨论。)

REST API 影响

本文档中描述的更改将需要 API 版本升级。

镜像发现

如果您只想列出所有社区镜像,并且仅列出社区镜像,那么您将使用

GET /v2/images?visibility=community

将尊重所有其他适当的过滤器。 值得注意的是 owner 参数的使用。 当与 visibility=community 过滤器一起提供时,这允许用户请求仅由该特定租户拥有的那些社区镜像

GET /v2/images?visibility=community&owner={owner_tenant_id}

请注意,visiblity 将被视为镜像对象的核心属性,因此将包含在通过 v2 接口生成的镜像列表中。

将镜像设为“社区镜像”

正如新的 policy.json 规则 communitize_images 允许的那样,管理员或所有者将使用现有的 image-update 调用将镜像的 visiblity 更改为 'community'

PATCH /v2/images/{image_id}

请求主体

[{ "op": "replace", "path": "/visibility", "value": "community" }]

响应和其他行为与之前为该调用定义的一样。

删除镜像的社区级别访问权限

镜像的管理员或所有者可以通过使用 image-update 调用从镜像中删除社区级别访问权限。 例如,与其像以前一样将其设置为 'community',不如将其设置为 'private'

PATCH /v2/images/{image_id}

请求主体

[{ "op": "replace", "path": "/visibility", "value": "private" }]

具有发布镜像权限的管理员或用户可以将社区可见性替换为 public

与上述情况一样,响应和其他行为与之前为该调用定义的一样。

安全影响

请参阅“其他部署者影响”。

通知影响

当前通知包含 is_public 属性,该属性仅当镜像是公共镜像时才为 true。 必须为了向后兼容性而保留此属性。

将为每个镜像添加一个额外的 visibility 属性,以指示其可见性,其可能值为 ['public', 'private', 'shared', 'community']

其他最终用户影响

与当前功能一致,如果在创建镜像时指定了可见性,将尊重该可见性。 如果创建镜像的用户没有适当的权限来设置镜像上的指定可见性值,则 image-create 调用将像现在一样失败。

默认可见性

为了与当前共享镜像的工作流程保持向后兼容性,镜像的默认 visibility 将为 shared

  • 具有共享可见性且没有镜像成员的镜像无法供镜像所有者以外的任何人使用,因此这符合当前行为。

  • 当前,当使用默认可见性创建镜像时,所有者可以立即向镜像添加成员以共享它。 通过将默认可见性值设置为 shared,我们保留了此行为。 如果默认可见性是 private,那么此行为将会中断,因为镜像所有者必须调用 API 才能将镜像的可见性更改为 shared,然后才能添加成员。

  • 虽然 Image API v1 在 Newton 中已弃用,但它仍然存在,并且很可能部署在 OpenStack 的 Ocata 版本中使用。 由于 v1 API 没有可见性的概念,因此用户无法更改可见性为 shared,以便镜像可以拥有成员并被成员访问。 通过将默认可见性设置为 shared,我们实现了以下期望的结果

    • v1 用户可以使用当前工作流程创建镜像并添加成员,即共享镜像只是在其上放置成员的问题。

    • 在同时使用 v1 和 v2 Image API 的部署中,如果使用 v1 创建镜像,然后在 v2 中发出 image-show 调用,则 shared 可见性可以清楚地表明 v2 用户镜像处于可以接受成员并可以被任何现有成员访问的状态。

如果默认可见性值不是 shared,那么需要修改 v1,以便 private 镜像可以接受成员并被成员访问。 但是,这将创建一个镜像根据您尝试通过 v1 或 v2 API 访问它而表现出根本不同的情况。 我们不想这样做。

一个关键问题是,将默认可见性从 private 更改为 shared 是否会对 v2 API 引入向后不兼容性。以下是三个论点,说明它不会

  1. 目前的情况是,由非管理员用户创建的镜像的 visibility 不等于 public。这种情况将被保留。

  2. 如果我们将默认可见性设置为 shared,那么实际上会引入向后不兼容性,因为任何当前创建镜像并立即向其添加成员的用户脚本都会中断。(事实上,这是早期版本规范中的一个问题。虽然 API 工作组同意这是一个“轻微”的向后不兼容性,可以接受,但他们指出 Glance 用户可能会对工作流程的改变感到恼火。 visibility 值的改变是两害相权取其轻。)

  3. 我们希望的情况是,当用户创建镜像而不指定可见性时,镜像的行为方式与现在相同。以这种方式表现的镜像将具有我们现在称为 shared 的可见性。这是可以预期的; 以前,我们没有足够的关键字来识别镜像的各种访问状态;现在我们有了,我们应该适当地使用这些关键字。

结论是,将默认可见性设置为 private 将比提议将默认可见性值设置为 shared 对最终用户产生更大的影响。

请注意,如果使用 v2 API 将镜像的可见性设置为 private,如果使用 v1 API 共享该镜像,则请求将失败,并返回 409 错误(如果对 v2 API 进行成员相关调用,也会发生这种情况)。这是合适的,因为有人明确禁用了该镜像的共享。

前面所述的一个最终要点值得明确说明。如果使用 v1 API 更新镜像的 is_public 属性,它将对镜像的可见性产生以下影响

  • 如果调用将 is_public 更新为 True,则镜像的相应 v2 visibility 将为 public

  • 如果调用将 is_public 设置为 False,则镜像的相应 v2 visibility 将设置为默认值,即 shared

这将保留 v1 API 中的向后兼容行为。先前提出的支持 shared 作为默认可见性的论点也适用于这种情况。

现有镜像可见性的迁移

数据库迁移应该如何进行的问题引起了足够的争议,以至于进行了一项运营商调查以收集数据 [SUR1]。调查结果在 [SUR2] 中报告(以及对迁移路径的建议,如果你继续阅读,你会发现最终被拒绝了)。简而言之,运营商对是否应该将所有可见性为 private 的镜像迁移到 shared,或者仅将具有成员的镜像迁移到 shared 意见不一。

我们对此进行了大量讨论(有关示例,请参阅 [GER1] 上的评论)。最终,我们达成的妥协反映在本规范中。这种迁移路径的主要理由是,值得牺牲一些预 Ocata 和 Ocata 镜像共享之间的轻微不兼容性,以避免最终用户在部署升级到 Ocata 后第一次使用 Glance 时感到困惑,突然发现他们拥有的所有镜像现在都具有“shared”的可见性。

因此,升级到 Glance Ocata 版本对最终用户的影响将如下

  • 任何现有的“private”镜像,如果具有非空成员列表,将被发现具有“shared”可见性。

  • 任何现有的“private”镜像,如果没有镜像成员,将被发现具有“private”可见性。

    • 不兼容性:v2:与预 Ocata 不同,必须将镜像的可见性更改为“shared”,然后才能成功进行 member-create 调用。v1:用户必须在进行 member-create 调用之前,显式将 is_public 设置为 False

  • 在升级后创建的任何未指定可见性的镜像都将具有“shared”可见性。

    • 不一致性:对于具有默认可见性的新创建的镜像,member-create 调用将立即起作用,而迁移的镜像则需要按照上述描述进行处理。

  • 在 Ocata 之前,最终用户拥有可能实际上是共享的“private”镜像。用户必须进行 API 调用才能查看成员列表,以确定其他用户是否可以访问该镜像。在 Ocata 中,最终用户将拥有可见性为 shared 的镜像,但实际上可能无法供其他用户访问。用户必须进行 API 调用才能查看镜像是否具有非空成员列表。

    • 不兼容性:确定镜像是否可供其他用户访问所需的流程大致相同,但现在取决于两个方面:(1)可见性必须为“shared”,以及(2)镜像的成员列表必须非空。

      这种不兼容性的好处是,在 Glance Ocata 版本中,visibilityprivate 实际上意味着“private”,而不是“private 或 shared”。

客户端更改

OpenStackClient 以及 python-glancelient 的库部分将被更新以公开此功能。CLI glanceclient 将不受支持。

用户可以使用 openstack image list --community 查看所有社区镜像。

将向 openstack image set 添加一个选项,名为 --visibility <VISIBILITY_STATUS>,其中 VISIBILTY_STATUS 可以是 {public, private, shared, community} 中的一个。

例如,要使镜像成为社区镜像

$ openstack image set --visibility community <IMAGE>

要再次使镜像私有

$ openstack image set --visibility private <IMAGE>

性能影响

其他部署者影响

使用 policy.json 调节创建社区镜像的能力。如上所述,将创建一个名为 communitize_image 的新规则,其默认配置为 [role:admin or rule:owner]

来自 Horizon 的用户也将能够通过单独的选项卡查看社区镜像。有关 Horizon 功能的详细信息,请参见:https://blueprints.launchpad.net/horizon/+spec/glance-community-images

开发人员影响

实现

负责人

主要负责人

timothy-symanczyk

评审人员

核心评审人

brian-rosmaita nikhil-komawar

工作项

  • 重构 db API 以使用 visibility 而不是 is_public

  • 向两个 db 后端的接口添加存储社区状态的功能

    • sqlalchemy

    • simple

  • 添加功能以启用此功能并使用 API 接受镜像

  • 添加单元测试以测试 API 的各种输入

  • 添加功能测试以测试社区镜像的生命周期

  • 更新 OpenStackClient 以使用新的 API 功能

  • 升级 API 版本

依赖项

测试

必须添加一个 tempest 测试来涵盖创建社区镜像及其在公共和私有状态之间的转换。

文档影响

新功能必须在 glance 和 OpenStackClient 中进行文档记录。

参考资料