创建 RequestSpec 对象

https://blueprints.launchpad.net/nova/+spec/request-spec-object

添加一个结构化、文档化的对象,用于表示在云中启动多个实例的规范。

问题描述

调度器的主要接口,select_destinations() 方法,接受一个 request_spec 参数,该参数是一个嵌套字典。该嵌套字典是在 nova.scheduler.utils.build_request_spec() 中构建的,但是请求规范的结构在任何地方都没有文档说明,并且调度器中的过滤器似乎对在调度期间查询该对象以及在 nova.scheduler.host_manager.HostStateManager.get_filtered_hosts() 方法的循环中修改 request_spec 对象采取了一种放任自流的态度,该方法调用过滤器的 host_passes 对象,并提供一个 filter_properties 参数,该参数本身包含一个名为 request_spec 的键,其中包含上述嵌套字典。

这种情况使得很难理解调度器中到底发生了什么,并且清理调度器接口中的此参数是为调度器代码的拆分做准备,以便创建一个正确版本化和正确文档化的接口的先决条件。

用例

这是一项纯粹的重构工作,旨在清理 Nova 和调度器之间的所有接口,以便在下一个周期中可以拆分调度器。

项目优先级

此蓝图是围绕“调度器”重构的全球努力的一部分,旨在帮助将其拆分。 这已被定义为本 Kilo 周期中的第三优先级。

提议的变更

将创建一个名为 RequestSpec 的新类,用于模拟请求启动多个虚拟机实例。 RequestSpec 对象的第一个版本将只是当前字典参数的对象化版本。 调度器将从 request_spec 字典本身构建此 RequestSpec 对象。

现有的 nova.scheduler.utils.build_request_spec 方法将被移除,以支持 nova.objects.request_spec.RequestSpec 上的一个工厂方法,该方法将从提供给 select_destinationsrequest_spec 参数中的现有键/值对构建一个 RequestSpec

备选方案

无。

数据模型影响

此规范不侧重于持久化 RequestSpec 对象,但将提出另一个蓝图(和一个规范),并将此规范作为依赖项,为 RequestSpec 对象提供一个 save() 方法,该方法将允许将其持久化在(可能)instance_extra DB 表中。

REST API 影响

无。

安全影响

无。

通知影响

无。

其他最终用户影响

无。

性能影响

无。

其他部署者影响

无。

开发人员影响

除了使调度器调用接口逐渐更容易阅读和理解之外,没有其他影响。

实现

当前由 nova-conductor 在调用 nova.scheduler.utils.build_request_spec() 函数时构建 request_spec 字典,其结构如下

def build_request_spec(ctxt, image, instances, instance_type=None):
   """Build a request_spec for the scheduler.

   The request_spec assumes that all instances to be scheduled are the same
   type.
   """
   instance = instances[0]
   if isinstance(instance, obj_base.NovaObject):
       instance = obj_base.obj_to_primitive(instance)

   if instance_type is None:
       instance_type = flavors.extract_flavor(instance)
   # NOTE(comstud): This is a bit ugly, but will get cleaned up when
   # we're passing an InstanceType internal object.
   extra_specs = db.flavor_extra_specs_get(ctxt, instance_type['flavorid'])
   instance_type['extra_specs'] = extra_specs
   request_spec = {
           'image': image or {},
           'instance_properties': instance,
           'instance_type': instance_type,
           'num_instances': len(instances),
           # NOTE(alaski): This should be removed as logic moves from the
           # scheduler to conductor.  Provides backwards compatibility now.
           'instance_uuids': [inst['uuid'] for inst in instances]}
   return jsonutils.to_primitive(request_spec)

为了尽可能接近从嵌套字典的键到对象属性表示法的直接转换,RequestSpec 类的一个可能的第一个版本类接口如下所示

class RequestSpec(base.NovaObject):

   """Models the request to launch one or more instances in the cloud."""

   VERSION = '1.0'

   fields = {
       'image': fields.DictOfStringsField(nullable=False),
       # instance_properties could eventually be deconstructed into component
       # parts
       'instance_properties': fields.ObjectField('Instance'),
       'instance_type': fields.ObjectField('Flavor', nullable=False),
       'num_instances': fields.IntegerField(default=1),
   }

由于 request_spec 字典中存在一个已弃用的 instance_uuids 字段,我们不会将此字段提供到 RequestSpec 对象中,而是将 RPC API 版本升级并将其从字典中删除,作为一项工作项目。

此蓝图不会更改 select_destinations 调度器 RPC API 方法,因此我们将在 nova-scheduler 端,从 FilterScheduler._schedule() 方法中的 request_spec 字典键构造一个 nova.objects.request_spec.RequestSpec 对象,如下所示

def _schedule(self, context, request_spec, filter_properties):
    """Returns a list of hosts that meet the required specs,
    ordered by their fitness.
    """
    elevated = context.elevated()
    request_spec = objects.RequestSpec.from_dict(request_spec)
    instance_type = request_spec.instance_type

    update_group_hosts = self._setup_instance_group(context,
            filter_properties)

    config_options = self._get_configuration_options()

    filter_properties.update({'context': context,
                              'request_spec': request_spec,
                              'config_options': config_options,
                              'instance_type': instance_type})

    self.populate_filter_properties(request_spec,
                                    filter_properties)

    # Find our local list of acceptable hosts by repeatedly
    # filtering and weighing our options. Each time we choose a
    # host, we virtually consume resources on it so subsequent
    # selections can adjust accordingly.

    # Note: remember, we are using an iterator here. So only
    # traverse this list once. This can bite you if the hosts
    # are being scanned in a filter or weighing function.
    hosts = self._get_all_host_states(elevated)

    selected_hosts = []
    if instance_uuids:
        num_instances = len(instance_uuids)
    else:
        num_instances = request_spec.num_instances
    for num in xrange(num_instances):
        # Filter local hosts based on requirements ...
        hosts = self.host_manager.get_filtered_hosts(hosts,
                filter_properties, index=num)

实施此蓝图的最后步骤是更改所有调度器过滤器,以便通过对象表示法而不是字典表示法访问原始属性。

如上文“数据模型影响”部分所述,此蓝图目前不打算持久化此对象。 我们在此蓝图中也尚未删除 filter_properties 字典。

负责人

主要负责人

bauzas

其他贡献者

工作项

  • request_spec 中删除 instance_uuids 字段,并为调度器 RPC API 升级版本

  • nova/scheduler/request_spec.py 中添加请求规范类,并附带单元测试

  • nova.scheduler.RequestSpec 上添加一个工厂类方法,该方法从传递给 nova.scheduler.utils.build_request_spec 函数的现有实例类型 extra_specs、scheduler_hints、flavor 和 image 对象集合中构造一个 RequestSpec 对象。

  • 修改 FilterScheduler._schedule() 以从提供的嵌套 request_spec 字典构造一个 RequestSpec 对象

  • 将所有过滤器类更改为对 RequestSpec 对象而不是嵌套的 request_spec 字典进行操作。

  • 添加开发人员参考文档,说明请求规范建模的内容。

依赖项

无。

测试

将添加新的请求规范对象单元测试。 现有的调度器过滤器单元测试将被修改,以访问 filter_properties 字典中的 RequestSpec 对象。

文档影响

更新任何可能引用旧字典访问的开发人员参考资料。

参考资料

此蓝图是整体工作的一部分,旨在清理、版本化和稳定 nova-api、nova-scheduler、nova-conductor 和 nova-compute 守护程序之间涉及调度和资源决策的接口。

有关 Kilo 目标蓝图的列表,请参阅 https://wiki.openstack.org/wiki/Gantt/kilo#Tasks