支持按禁止的聚合成员过滤

https://blueprints.launchpad.net/nova/+spec/negative-aggregate-membership

此蓝图建议支持底层资源提供程序的聚合成员的负面过滤。

问题描述

Placement 目前支持 member_of 查询参数,用于 GET /resource_providersGET /allocation_candidates 端点。该参数可以是“表示聚合 UUID 的字符串”,也可以是“以 in: 前缀开头,后跟逗号分隔的字符串列表,这些字符串表示聚合 UUID”。

例如,

&member_of=in:<agg1>,<agg2>&member_of=<agg3>

在逻辑上转换为

候选资源提供程序应位于 agg1 或 agg2 中的一个,但必须位于 agg3 中。(有关详细信息,请参阅 alloc-candidates-member-of 规范)

但是,API 中没有用于禁止聚合的表达式。换句话说,我们无法说“对于非特殊工作负载,不要使用此特殊聚合中的资源提供程序”。

用例

此功能对于为特定用户保留特殊资源非常有用。

用例 1

其中一些计算主机是 已许可的 Windows 计算主机,这意味着在此计算主机上启动的任何 VM 都将被视为已许可的 Windows 镜像,并且根据 VM 的使用情况,运营商将向最终用户收取费用。作为运营商,我希望避免在 已许可的 Windows 计算主机 上启动其他操作系统(Windows 操作系统)的镜像/卷。

用例 2

像 blazar 这样的预留项目希望为其主机预留拥有自己的聚合,以便没有预留的消费者可以被安排在该聚合之外,从而节省预留资源。

提议的变更

调整 member_of 参数的处理方式,以便可以将聚合表示为禁止的。禁止的聚合以前缀 ! 开头。

在以下示例中,

&member_of=!<agg1>

在逻辑上转换为

候选资源提供程序应位于 agg1 中。

此负面表达式也可以在多个 member_of 参数中使用

&member_of=in:<agg1>,<agg2>&member_of=<agg3>&member_of=!<agg4>

在逻辑上转换为

候选资源提供程序必须是 agg1 或 agg2 中的至少一个,必须位于 agg3 中,并且位于 agg4 中。

请注意,我们不支持在 in: 前缀中使用 !

&member_of=in:<agg1>,<agg2>,!<agg3>

将导致 HTTP 400 Bad Request 错误。

相反,我们支持 !in: 前缀

&member_of=!in:<agg1>,<agg2>,<agg3>

这等效于

member_of=!<agg1>&member_of=!<agg2>&member_of=!<agg3>

嵌套资源提供程序

对于嵌套资源提供程序,根提供程序上的聚合会自动跨越整个树。当根提供程序位于禁止的聚合中时,即使子提供程序不属于任何(或另一个不同的)聚合,子提供程序也无法成为候选者。

在以下环境中,例如,

                                     +-----------------------+
                                     | sharing storage (ss1) |
                                     |   agg: [aggB]         |
                                     +-----------+-----------+
                                                 | aggB
+------------------------------+  +--------------|--------------+
| +--------------------------+ |  | +------------+------------+ |
| | compute node (cn1)       | |  | |compute node (cn2)       | |
| |   agg: [aggA]            | |  | |  agg: [aggB]            | |
| +-----+-------------+------+ |  | +----+-------------+------+ |
|       | parent      | parent |  |      | parent      | parent |
| +-----+------+ +----+------+ |  | +----+------+ +----+------+ |
| | numa1_1    | | numa1_2   | |  | | numa2_1   | | numa2_2   | |
| |  agg:[aggC]| |   agg:[]  | |  | |   agg:[]  | |   agg:[]  | |
| +-----+------+ +-----------+ |  | +-----------+ +-----------+ |
+-------|----------------------+  +-----------------------------+
        | aggC
  +-----+-----------------+
  | sharing storage (ss2) |
  |   agg: [aggC]         |
  +-----------------------+

排除约束如下

  • member_of=!<aggA> 排除“cn1”、“numa1_1”和“numa1_2”。

  • member_of=!<aggB> 排除“cn2”、“numa2_1”、“numa2_2”和“ss1”。

  • member_of=!<aggC> 排除“numa1_1”和“ss2”。

请注意,这种跨越不会发生在编号的 member_of 参数上,该参数用于粒度请求

  • member_of<N>=!<aggA> 排除“cn1”

  • member_of<N>=!<aggB> 排除“cn2”和“ss1”

  • member_of<N>=!<aggC> 排除“numa1_1”和“ss2”。

有关详细信息,请参阅 granular-resource-request 规范。

备选方案

我们可以使用禁止的特性来排除特定的资源提供程序,但如果我们使用特性,那么我们应该将 Blazar 或 windows 许可特性不仅放在根提供程序上,而且放在树中的每个资源提供程序上,所以我们不这样做。

我们还可以创建 nova 调度器过滤器来通过查看主机聚合关系对计算主机进行后处理,就像 BlazarFilter 今天所做的那样。但是,这效率低下,我们不想为 windows 许可用例开发/使用另一个过滤器。

数据模型影响

无。

REST API 影响

将创建一个新的微版本,该版本将更新 GET /allocation_candidatesGET /resource_providersmember_of 参数的验证,以接受 ! 作为聚合 UUID 的前缀,以及作为 in: 前缀的前缀,以表示需要排除前缀聚合(或聚合)。

安全影响

无。

通知影响

无。

其他最终用户影响

无。

性能影响

对数据库的查询将增加适度的复杂性,但现有的表索引应该可以很好地处理此问题。

其他部署者影响

无。

开发人员影响

这有助于我们开发一个简单的预留机制,而无需使用特定的 nova 过滤器,例如通过以下流程

  1. 希望启用 blazar 的运营商在 nova.conf 中设置默认的禁止和必需的成员资格键。

    • 配置文件中的参数键类似于 [scheduler]/placement_req_default_forbidden_member_prefix,并且该值由运营商设置为 reservation:

    • 配置文件中的参数键类似于 [scheduler]/placement_req_required_member_prefix,并且该值由运营商设置为 reservation:

  2. 运营商启动服务并通过 blazar API 创建一个主机池以进行预留

    • Blazar 在初始化时创建一个 nova 聚合,其元数据为 reservation:<random_id>,作为 blazar 的空闲池

    • Blazar 按照运营商的要求将主机放入空闲池聚合中

  3. 用户使用 blazar 进行主机预留并获取预留 ID

    • Blazar 从 blazar 的空闲池中选择一个主机

    • Blazar 为该预留创建一个新的 nova 聚合,并将该聚合的元数据键设置为 reservation:<resv_id>,并将预留主机放入该聚合中

  4. 用户使用具有 reservation:<resv_id> 元数据/extra_specs 的 flavor/image 创建 VM 以消耗预留

    • Nova 在 flavor 中发现 extra_spec 具有以 [scheduler]/placement_req_required_member_prefix 中设置的内容开头的键,并查找具有指定元数据的表

      required_prefix = CONF.scheduler.placement_req_required_member_prefix
      # required_prefix = 'reservation:'
      required_meta_data = get_flavor_extra_spec_starts_with(required_prefix)
      # required_meta_data = 'reservation:<resv_id>'
      required_aggs = aggs_whose_metadata_is(required_meta_data)
      # required_aggs = [<An aggregate for the reservation>]
      
    • Nova 发现默认禁止聚合元数据前缀,在 [scheduler]/placement_req_default_forbidden_member_prefix 中设置,通过 flavor 显式指定,因此跳过

      default_forbidden_prefix = CONF.scheduler.placement_req_default_forbidden_member_prefix
      # default_forbidden_prefix = ['reservation:']
      forbidden_aggs = set()
      if not get_flavor_extra_spec_starts_with(default_forbidden_prefix):
          # this is skipped because 'reservation:' is in the flavor in this case
          forbidden_aggs = aggs_whose_metadata_starts_with(default_forbidden_prefix)
      
    • Nova 使用必需的和禁止的聚合调用 placement

      # We don't have forbidden aggregates in this case
      ?member_of=<required_aggs>
      
  5. 用户使用不带预留的 flavor/image 创建 VM,即不带 reservation:<resv_id> 元数据/extra_specs。

    • Nova 在 flavor 中发现 extra_spec 没有以 [scheduler]/placement_req_required_member_prefix 中设置的内容开头的键,因此没有获得必需的聚合

      required_prefix = CONF.scheduler.placement_req_required_member_prefix
      # required_prefix = 'reservation:'
      required_meta_data = get_flavor_extra_spec_starts_with(required_prefix)
      # required_meta_data = ''
      required_aggs = aggs_whose_metadata_is(required_meta_data)
      # required_aggs = set()
      
    • Nova 查找元数据以 [scheduler]/placement_req_default_forbidden_member_prefix 中设置的内容开头的默认禁止聚合

      default_forbidden_prefix = CONF.scheduler.placement_req_default_forbidden_member_prefix
      # default_forbidden_prefix = ['reservation:']
      forbidden_aggs = set()
      if not get_flavor_extra_spec_starts_with(default_forbidden_prefix):
          # This is not skipped now
          forbidden_aggs = aggs_whose_metadata_starts_with(default_forbidden_prefix)
      # forbidden_aggs = <blazar's free pool aggregates and the other reservation aggs>
      
    • Nova 使用必需的和禁止的聚合调用 placement

      # We don't have required aggregates in this case
      ?member_of=!in:<forbidden_aggs>
      

请注意,nova 配置文件中的更改和请求过滤器中的更改只是一个示例,不在此规范的范围内。另一种方法是让 placement 了解默认的禁止特性/聚合(请参阅 Bi-directional enforcement of traits 规范)。但是,我们同意负责实例禁止/必需的特性/聚合的是 nova 而不是 placement。

升级影响

无。

实现

负责人

主要负责人

中村 哲郎 (nakamura.tetsuro@lab.ntt.co.jp)

工作项

  • 更新 ResourceProviderList.get_all_by_filtersAllocationCandidates.get_by_requests 方法,以更改数据库查询以过滤“不是此聚合”。

  • 更新 placement API 处理程序,用于 GET /resource_providersGET /allocation_candidates,在一个新的微版本中将负面聚合传递给上述步骤中更改的方法,包括输入验证调整。

  • 添加对修改后的数据库查询的功能测试。

  • 添加 gabbi 测试,以表达新的查询,包括成功的查询和应该导致 400 响应的查询。

  • API 更改的发布说明。

  • 更新微版本文档以指示新版本。

  • 更新 placement-api-ref 以显示新的查询处理方式。

依赖项

无。

测试

正常的函数和单元测试。

文档影响

记录 REST API 微版本到适当的参考文档中。

参考资料