为驱动程序私有数据添加数据库表

https://blueprints.launchpad.net/cinder/+spec/driver-private-data

正如在年中会议上讨论的那样(https://etherpad.openstack.org/p/cinder- meetup-winter-2015),我们希望添加一个数据库表,驱动程序可以在其中存储它们运行所需的信息。一个具体的用例是用于发起方的 iSCSI CHAP 凭据,但它应该足够通用,以便其他驱动程序也可以存储它们所依赖的数据。

问题描述

目前,驱动程序没有标准的解决方案来存储它们运行所需的信息。对于与卷相关的事务,我们可以在卷表上使用一些“提供者”字段,但对于与卷生命周期无关的事务,这些字段是不够的。这导致一些驱动程序将信息存储在后端,这对于某些后端有效,但并非所有后端都具有此功能。

此用例之一是存储目标 CHAP 密钥,以便 iSCSI 发起方可以对目标进行 CHAP 身份验证。

用例

提议的变更

解决此问题的方法是在 Cinder 数据库中创建一个新表,该表可以保存驱动程序的发起方信息。模型如下所示

+--------------+--------------+--------------------------------------------+
|   Field      |     Type     |            Description                     |
+--------------+--------------+--------------------------------------------+
| created_at   | datetime     |                                            |
| updated_at   | datetime     |                                            |
| id           | int(11)      | Auto incremented ID                        |
| initiator    | varchar(255) | Unique constraint with "key" +             |
|              |              | "namespace"                                |
| namespace    | varchar(255) | Unique constraint with "key" + "initiator" |
|              |              | This comes from cinder.conf for each back  |
|              |              | end configuration.                         |
| key          | varchar(255) | Unique constraint with "initiator" +       |
|              |              | "namespace"                                |
| value        | varchar(255) | This could be anything a driver wants to   |
|              |              | keep track of for this initiator. In the   |
|              |              | CHAP credential usecase this could be a    |
|              |              | barbican key uuid.                         |
+--------------+--------------+--------------------------------------------+

在 VolumeManager 的 initialize_connection 中,如果存在发起方(在 iSCSI 的情况下),我们可以查找它的行,按命名空间进行过滤,并将它们作为 initialize_connection 的一个新的可选参数传递给驱动程序。

命名空间字段将来自驱动程序基类上的一个新的配置选项,称为 driver_data_namespace。此配置选项默认为 None。如果未设置此选项,我们将检查 backend_name 配置选项,最后,如果未设置该选项,我们将回退到驱动程序类名。这将允许在多个不同的后端和单个后端在 HA 配置中共享数据。

然后,驱动程序可以返回一个模型更新字典作为连接信息上的“initiator_update”字段。这些更改将应用于发起方表。

initiator_update 字典可以包含“set_values”字典和/或“remove_values”列表。set_values 是键值对,用于在数据库中设置或更新。remove_values 是要从数据库中删除的键的列表。它们将被“硬”删除,因此一旦在 remove_values 中指定,数据将不再保留在数据库中。

initialize_connection 的返回值可能如下所示

{
    "driver_volume_type": "iscsi",
    "data": {
        "target_iqn": "iqn.2010-06.com.purestorage:flasharray.12345abc",
        "target_portal": 1.2.3.4:5678,
        "target_lun": 1,
        "target_discovered": True,
        "access_mode": "rw",
        "auth_method": "CHAP",
        "auth_username": "someUserName",
        "auth_password": "somePassword"
    },
    "initiator_updates":
        {
            "set_values": {
                "purearray1ChapSecretId": "someUUID",
                "someOtherThing": "someOtherValue"
            }
            "remove_values": [
                "myOldKey",
                "anotherKey"
            ]
        }
}

管理器 initialize_connection 方法然后可以从返回对象中提取更新,执行数据库操作(如果需要),并将连接信息传递出去,同时删除该字段,以防止其在系统中传播。

备选方案

  • 这些键值对是针对发起方范围的,因此有两个不同的后端可以访问这些值。这对于配置为主动/主动 HA 的后端很有帮助,但存在两个不同的供应商被同一个发起方使用并发生密钥冲突的风险。我们可以引入一个 vendor-prefix 字段作为密钥的一部分来避免这种情况。

    • 我们也可以使用 cinder 主机来代替 vendor 前缀来限定这些数据

  • 我们可以让驱动程序在 Cinder 之外跟踪这些数据。这可以留给驱动程序后端来存储数据,或者使用另一个外部数据库。这种方法的缺点包括维护、更新和保持同步的不同数据库,这将对云管理员来说不是理想的用户体验,驱动程序中多种同步/锁定方式可能会并将会引入错误和维护问题,并且使新的驱动程序开发人员更难做出关于数据存储位置的良好选择。

  • 我们可以允许驱动程序通过一些 get/set api 直接(ish)访问数据库来存储任意键值对。在该模型中,数据库表的替代方案包括

    • 有一个用于主机名的列,并将其用作“key”的唯一约束的一部分。好处是使用这种方法不太可能发生驱动程序之间的命名冲突。缺点是使用相同驱动程序的两个主机可能无法轻松共享数据,这使得同一后端的活跃/活跃 HA 变得困难。

    • 有一个用于驱动程序类名的列,并将其用作“key”的唯一约束的一部分。再次,好处是帮助避免密钥字段的冲突。这一个对于共享使用相同驱动程序类型的宿主机更好,但问题在于名称从哪里来?如果由驱动程序报告为字符串,那么它可能就像任何通用前缀一样,因为没有人可以阻止别人随意命名。如果从驱动程序对象上的 python 内部变量中自动收集,那么对于稍后更改类名的驱动程序来说,就会出现问题。

    • 有一个列只是提供者/供应商命名空间,并且是“key”的唯一约束的一部分。目标再次是避免密钥字段的冲突。与上一个类似,唯一的缺点是它来自哪里,以及没有真正可以阻止驱动程序使用相同命名空间的东西,但它将是另一层保护。

    • 没有限制,让键更像一个全局范围,这允许驱动程序轻松共享数据,但存在冲突问题。

  • 返回一个元组而不是将数据添加到 initialize_connection 的返回对象中。这需要一个可选的第二个返回值和条件调用/返回值,或者修改驱动程序.initialize_connection(...) 的每次使用。

数据模型影响

建议的解决方案是添加一个名为 driver_initiator_data 的新数据库表和相关的模型 DriverInitiatorData。将有一个迁移来添加和删除表,但不会播种或修改任何数据。

将通过 Proposed Change 中概述的 api 访问数据库。

表行将被硬删除,因此我们不会保留潜在的敏感凭据/信息。

REST API 影响

安全影响

通知影响

其他最终用户影响

性能影响

潜在影响是驱动程序大量利用此表进行额外的数据库查询。

其他部署者影响

每个后端可能需要设置的新配置选项。

开发人员影响

将数据存储在其后端上的驱动程序可能希望使用此功能。

此更改需要修改所有驱动程序 initialize_connection 方法实现的签名。

实现

负责人

主要负责人

patrick-east

其他贡献者

感兴趣的各方

walter-boring

工作项

  • 添加新的模型

  • 添加新的数据库 API

  • 添加新的数据库迁移脚本

  • 修改卷管理器 initialize_connection 方法以查询/保存数据

依赖项

测试

  • 数据库迁移测试

  • 数据库 API 测试

  • BaseDriver 单元测试

  • 没有新的 tempest 测试

文档影响

参考资料