Share 迁移 Ocata 改进

https://blueprints.launchpad.net/manila/+spec/ocata-migration-improvements

基于巴塞罗那峰会反馈的 Share 迁移改进和变更提案。

问题描述

在巴塞罗那峰会上,我们收集了关于 Share 迁移实验 API 功能 [1] 的反馈。 提出了一些改进建议

  • 对于驱动程序辅助迁移,驱动程序可能能够迁移 share 及其快照,但 API 层在存在快照时会阻止请求。 只有在使用 “force-host-assisted-migration” 参数时,才应该出现这种情况。

  • 关于 “nondisruptive” 参数的默认值是应该为 True 还是 False,一直存在很多争论。 目前它也与所有其他驱动程序辅助相关参数不一致,默认值为 False,而所有其他驱动程序辅助参数默认值为 True。

  • 在 manila-ui 的“启动迁移”表单中,驱动程序辅助迁移 API 参数可以被灰显,以便用户更好地理解 “强制主机辅助迁移” 会抵消其他参数的效果。

  • 目前,API 不会在 “force-host-assisted-migration” 和其他驱动程序辅助参数都设置为 True 的明显错误组合时立即返回错误。 这在未使用 manila-ui 时会很有用。

  • manila-ui 和 python-manilaclient 中驱动程序辅助迁移 API 参数的描述可以改进,以说明这些参数是“强制执行的”,而不是在未选择这些属性时“不保留元数据”。

  • 目前 Share 迁移 API 禁止迁移到相同的存储池。 这会阻止 share 服务器之间的 share 迁移或仅仅是重新类型化。 在这种情况下,希望 share 仍然位于相同的存储池中,同时能够更改 share 的类型或 share 网络(从而相应地更改 share 服务器)。

用例

所提议的变更实现解决了现有问题并实现了以下用例

  • 迁移 share 及其快照

  • 在选择 API 参数时获得更好的用户体验

  • share 服务器之间的负载均衡

  • 重新类型化 share

提议的变更

本规范提出

  1. 在 “force-host-assisted-migration” 为 False 时,从 API 层移除快照限制,将其更改为像其他参数验证一样工作(可写、preserve_metadata 等),从而为 migration_check_compatibility 驱动程序接口添加一个额外的条目。 将该参数添加到 API 层,命名为 “preserve_snapshots”。 此外,与其他参数一样,该参数不适用于主机辅助迁移,如果启用该参数,则会阻止其运行。

  2. 将所有 REST API 参数更改为必需,除了 “force-host-assisted-migration”。 这样做的目的是为了更好地支持未来的 REST API,在旧的微版本中使用时将不存在的新 REST API 参数默认为 False,并在管理员意外未指定参数时避免数据丢失。 有关更多信息,请参阅参考 [2]

  3. 当 “force-host-assisted-migration” API 参数与其他驱动程序辅助迁移 API 参数一起设置时,返回错误。

  4. 改进 manila-ui 和 python-manilaclient 中驱动程序辅助 API 参数的描述,以包含 “强制…” 在名称中。 例如:“强制可写”。 请注意,这不会更改参数名称。

  5. 更改验证,拒绝 API 请求,当参数与源 share 相同。 正确的行为是,如果请求的参数与 share 的属性匹配,则返回 HTTP 200 “SUCCESS”,已经具有相同的 “share 网络”、“share 类型” 和 “pool”。

  6. 当 “access_rules_status” 处于 “ERROR” 状态时,不应允许迁移开始。 这会导致任何迁移失败,应在 API 层阻止。 本规范建议为此添加验证。

  7. 由于该实例无法挂载(因为在迁移期间无法添加新的访问规则),因此在迁移期间不应向目标 share 实例显示导出位置,从而使无法确定哪个可挂载的用户感到困惑,而用户可以看到两个导出位置。

  8. 随着在 Share 模型中保存的 mount_snapshot_support、revert_to_snapshot_support 和 create_share_from_snapshot_support 等可选 extra_specs 的添加,这导致需要在迁移后更新这些属性,以匹配已更改的目标实例 share_type(如果已更改)。

备选方案

已经讨论了一种替代方案,关于上述提案 #2,其中所有选项都将更改为 False。 这将意味着默认的 migration-start 命令将能够运行主机辅助迁移,如果驱动程序辅助方法不受支持或失败。 我们决定不采用这种方法,因为我们最重要的是避免在执行迁移时出现意外情况。 我们还考虑将所有驱动程序辅助参数默认为 True,以避免意外情况,但这会损害 REST API 对未来更新的合理性。 我们通过坚持客户端始终为每个选项发送 True 或 False 值来解决这些问题。 有关更多详细信息,请参阅 [2]

数据模型影响

REST API 影响

  • 微版本将增加以包含所提议的更改。 由于我们将仅支持具有必需参数的那个版本,因此将放弃对早期迁移-start 微版本的支持。

  • 所提议的更改将更新 migration_start API,如下所示

    • (POST, 200, 202, 400, 409) migration-start: 迁移 share URL: /shares/<share_id>/action Body

      {
        'migration_start': {
          'force_host_assisted_migration': false,
          'preserve_metadata': true,
          'writable': true,
          'nondisruptive': true,
          'preserve_snapshots': true,
          'host': 'ubuntu@generic2#GENERIC2',
          'new_share_type_id': 'foo_share_type_id',
          'new_share_network_id': 'bar_share_network_id'
        }
      }
      
  • 除非设置了 “强制主机辅助迁移” API 参数选项,否则此 API 不会立即返回错误,即使 share 具有快照。

  • 现在,未指定 “nondisruptive”、“preserve_snapshots”、“writable” 或 “preserve_metadata” 的任何值将返回错误 400。

  • 相同的池 API 限制将更改为在 “目标主机”、“share 网络” 和 “share 类型” 与源 share 相同时返回 200。

  • 将 “force_host_assisted_migration” 设置为 True,同时 “writable”、“preserve_metadata”、“preserve_snapshots” 或 “nondisruptive” 中的任何一个设置为 True,也将导致错误 400,因为这将是不兼容的组合。

  • 当 “access_rules_status” 处于 “ERROR” 状态时尝试迁移将返回错误 400。

  • 目标迁移实例的导出位置不再显示,直到迁移完成。

安全影响

通知影响

其他最终用户影响

本提案需要更新 python-manilaclient。 请参阅示例

manila migration-start <share> <host> --nondisruptive <True|False>
--writable <True|False> --preserve-metadata <True|False>
--preserve-snapshots <True|False> [--new-share-network <share_net>]
[--new-share-type <share_type>]
[--force-host-assisted-migration <True|False>]

manila migration-start share_1 ubuntu@generic1#GENERIC1 --writable True
--nondisruptive False --preserve-metadata True --preserve-snapshots False

对于 manila-ui,将有一个新的复选框 “保留快照”。

性能影响

其他部署者影响

开发人员影响

驱动程序影响

将提示驱动程序维护者根据引入的新 API 参数 ‘preserve_snapshots’ 更新其驱动程序辅助迁移 “migration_check_compatibility” 实现。 在此更改中,它将作为 “False” 添加到现有实现中。

所有现有的迁移驱动程序接口都将更新,以包含与快照相关的参数。 请参阅以下新的更新接口

def migration_start(
        self, context, source_share, destination_share,
        source_snapshots, snapshot_mappings, share_server=None,
        destination_share_server=None):
    """Starts migration of a given share to another host.

    .. note::
       Is called in source share's backend to start migration.

    Driver should implement this method if willing to perform migration
    in a driver-assisted way, useful for when source share's backend driver
    is compatible with destination backend driver. This method should
    start the migration procedure in the backend and end. Following steps
    should be done in 'migration_continue'.

    :param context: The 'context.RequestContext' object for the request.
    :param source_share: Reference to the original share model.
    :param destination_share: Reference to the share model to be used by
        migrated share.
    :param source_snapshots: List of snapshots owned by the source share.
    :param snapshot_mappings: Mapping of source snapshot IDs to
        destination snapshot models.
    :param share_server: Share server model or None.
    :param destination_share_server: Destination Share server model or
        None.
    """
    raise NotImplementedError()

def migration_continue(
        self, context, source_share, destination_share, source_snapshots,
        snapshot_mappings, share_server=None,
        destination_share_server=None):
    """Continues migration of a given share to another host.

    .. note::
        Is called in source share's backend to continue migration.

    Driver should implement this method to continue monitor the migration
    progress in storage and perform following steps until 1st phase is
    completed.

    :param context: The 'context.RequestContext' object for the request.
    :param source_share: Reference to the original share model.
    :param destination_share: Reference to the share model to be used by
        migrated share.
    :param source_snapshots: List of snapshots owned by the source share.
    :param snapshot_mappings: Mapping of source snapshot IDs to
        destination snapshot models.
    :param share_server: Share server model or None.
    :param destination_share_server: Destination Share server model or
        None.
    :return: Boolean value to indicate if 1st phase is finished.
    """
    raise NotImplementedError()

def migration_complete(
        self, context, source_share, destination_share, source_snapshots,
        snapshot_mappings, share_server=None,
        destination_share_server=None):
    """Completes migration of a given share to another host.

    .. note::
        Is called in source share's backend to complete migration.

    If driver is implementing 2-phase migration, this method should
    perform the disruptive tasks related to the 2nd phase of migration,
    thus completing it. Driver should also delete all original share data
    from source backend.

    :param context: The 'context.RequestContext' object for the request.
    :param source_share: Reference to the original share model.
    :param destination_share: Reference to the share model to be used by
        migrated share.
    :param source_snapshots: List of snapshots owned by the source share.
    :param snapshot_mappings: Mapping of source snapshot IDs to
        destination snapshot models.
    :param share_server: Share server model or None.
    :param destination_share_server: Destination Share server model or
        None.
    :return: If the migration changes the export locations or snapshot
        provider locations, this method should return a dictionary with
        the relevant info. In such case, a dictionary containing a list of
        export locations and a list of model updates for each snapshot
        indexed by their IDs.

        Example::

            {
                'export_locations':
                [
                    {
                    'path': '1.2.3.4:/foo',
                    'metadata': {},
                    'is_admin_only': False
                    },
                    {
                    'path': '5.6.7.8:/foo',
                    'metadata': {},
                    'is_admin_only': True
                    },
                ],
                'snapshot_updates':
                {
                    'bc4e3b28-0832-4168-b688-67fdc3e9d408':
                    {
                    'provider_location': '/snapshots/foo/bar_1'
                    },
                    '2e62b7ea-4e30-445f-bc05-fd523ca62941':
                    {
                    'provider_location': '/snapshots/foo/bar_2'
                    },
                },
            }

    """
    raise NotImplementedError()

def migration_cancel(
        self, context, source_share, destination_share, source_snapshots,
        snapshot_mappings, share_server=None,
        destination_share_server=None):
    """Cancels migration of a given share to another host.

    .. note::
       Is called in source share's backend to cancel migration.

    If possible, driver can implement a way to cancel an in-progress
    migration.

    :param context: The 'context.RequestContext' object for the request.
    :param source_share: Reference to the original share model.
    :param destination_share: Reference to the share model to be used by
        migrated share.
    :param source_snapshots: List of snapshots owned by the source share.
    :param snapshot_mappings: Mapping of source snapshot IDs to
        destination snapshot models.
    :param share_server: Share server model or None.
    :param destination_share_server: Destination Share server model or
        None.
    """
    raise NotImplementedError()

def migration_get_progress(
        self, context, source_share, destination_share, source_snapshots,
        snapshot_mappings, share_server=None,
        destination_share_server=None):
    """Obtains progress of migration of a given share to another host.

    .. note::
        Is called in source share's backend to obtain migration progress.

    If possible, driver can implement a way to return migration progress
    information.
    :param context: The 'context.RequestContext' object for the request.
    :param source_share: Reference to the original share model.
    :param destination_share: Reference to the share model to be used by
        migrated share.
    :param source_snapshots: List of snapshots owned by the source share.
    :param snapshot_mappings: Mapping of source snapshot IDs to
        destination snapshot models.
    :param share_server: Share server model or None.
    :param destination_share_server: Destination Share server model or
        None.
    :return: A dictionary with at least 'total_progress' field containing
        the percentage value.
    """
    raise NotImplementedError()

如上所述,migration_complete 驱动程序接口的返回值已更改为返回一个字典结构,其中包含导出位置和快照更新的字典,其中包含每个快照的模型更新,以便在 manila 的数据库中更新提供程序位置。

实现

在启动驱动程序辅助迁移时,将检查驱动程序是否支持 “preserve_snapshots”,无论 API 选项如何,因为正在迁移的 share 可能具有现有的快照。 如果驱动程序不支持 “preserve_snapshots”,将引发错误消息,说明驱动程序辅助迁移无法在 share 具有快照时进行。

如果驱动程序支持 “preserve_snapshots”,将检查所有现有的快照是否具有“可用”状态。 如果是,将在数据库中创建与每个源快照实例对应的目标快照实例。 最后,将源快照实例列表和映射字典传递给驱动程序,该字典由源快照实例 ID 索引的目标快照实例组成,以便在更新的驱动程序接口中。

在以后的阶段(例如调用 migration_continue 和 migration_complete)可以轻松地从数据库中检索此映射和快照列表。 迁移完成后,将根据驱动程序返回的模型更新(例如 “provider_location” 和 “export_locations”)更新快照实例。

对于主机辅助迁移,API 层中存在的快照验证已复制到开始主机辅助迁移之前,因为它将阻止主机辅助迁移运行。

最后,share 模型的可选 extra_specs 会根据目标 share 类型进行更新。

负责人

主要负责人

ganso

工作项

  • 实现 manila 的主要补丁,包括

    • 更新的 Tempest 测试

    • 更新的单元测试

    • 本提案中描述的 API 变更

    • Share 迁移主机辅助和驱动程序辅助变更所需

  • 使用以下内容实现 python-manilaclient 中的添加和变更

    • 单元测试

    • 功能测试

  • 使用以下内容实现 manila-ui 中的添加和变更

    • 单元测试

  • 更新此功能的文档(请参阅 文档影响 部分)

依赖项

到目前为止,没有以前的依赖于其他 Ocata 补丁

测试

  • manila、manila-ui 和 python-manilaclient 中的单元测试

  • manila 和 python-manilaclient 中的 Tempest API 测试

文档影响

  • 文档字符串

  • 发布说明

  • 开发者参考

  • 管理员参考

  • API 参考

参考资料

[1] https://etherpad.openstack.org/p/ocata-manila-contributor-meetup

[2] http://lists.openstack.org/pipermail/openstack-dev/2016-November/107186.html