为 Ironic 节点添加卷连接信息

https://bugs.launchpad.net/ironic/+bug/1526231

此 RFE 引入 Ironic 中的更改,以支持连接和从远程卷启动实例。

问题描述

当用户使用 Cinder 卷启动裸机实例时,Nova 会协调与 Cinder 和 Ironic 的通信。启动过程的工作流程如下

  1. (准备) 管理员使用发起者信息注册节点。

  2. 用户要求 Cinder 创建启动卷。

  3. 用户要求 Nova 从 Cinder 卷启动节点。

  4. Nova 调用 Ironic 以收集 iSCSI/FC 发起者信息。Ironic 收集发起者信息并将其返回给 Nova。

  5. Nova 调用 Cinder 将卷连接到节点。Cinder 将卷连接到节点并返回连接信息,其中包括目标信息。

  6. Nova 将节点的 target 信息传递给 Ironic

  7. Nova 调用 Ironic 启动实例。Ironic 准备裸机节点从远程卷启动,该远程卷由 target 信息标识,并打开裸机节点的电源。

在上述工作流程中,Nova 调用 Ironic 以获取/设置发起者/target 信息(第 4 步和第 6 步),并且管理员调用 Ironic 以设置发起者信息(第 1 步),但目前 Ironic 既没有这些信息,也没有用于这些信息的 API。

提议的变更

  • 添加一个名为 volume_connectors 的新表,包含以下字段

    • id

      • Integer

      • PrimaryKeyConstraint

    • uuid

      • String(长度=36)

      • UniqueConstraint

    • node_id

      • Integer

      • ForeignKeyConstraint(‘nodes.id’)

    • created_at

      • DateTime

    • updated_at

      • DateTime

    • type(可以具有的值:“iqn”、“ip”、“mac”、“wwnn”、“wwpn”、“net-id”)

      • String(长度=32)

      • UniqueConstraint(type, connector_id)

    • connector_id

      • String(长度=255)

      • UniqueConstraint(type, connector_id)

    • extra

      • Text

    注意

    Ironic 应该允许用户在 connector_id 字段中设置硬编码的 IP 和/或 MAC 地址,因为我们不能对存储网络做任何假设。它可能是一个 Neutron 网络,或者可能是一个 OpenStack 部署部分不知道的网络。这取决于部署。

    注意

    extra 字段在数据库中是文本,但在对象中是一个字典(JSON 编码的字典)

  • 添加一个名为 volume_targets 的新表,包含以下字段

    • id

      • Integer

      • PrimaryKeyConstraint

    • uuid

      • String(长度=36)

      • UniqueConstraint

    • node_id

      • Integer

      • ForeignKeyConstraint(‘nodes.id’)

    • created_at

      • DateTime

    • updated_at

      • DateTime

    • volume_type

      • String(长度=64)

    • properties

      • Text

    • boot_index

      • Integer

      • UniqueConstraint(node_id, boot_index)

      • 这用于 Ironic 区分根卷。与 Nova 类似,Ironic 假定 boot index 为 0 的卷是根设备。(Nova 将 boot index 与每个块设备关联,并假定 boot index 为 0 的卷是根卷。)

    • volume_id

      • String(长度=36)

    • extra

      • Text

    注意

    Ironic 应该在节点 tear_down 时清除连接的 target 信息,就像它对 instance_info 所做的那样。

    注意

    propertiesextra 在数据库中是文本,但在对象中是一个字典(JSON 编码的字典)

    注意

    properties 字段的内容取决于卷类型。应在 Bare Metal API 文档中添加参考信息

    对于 iSCSI 示例

    {"auth_method": "CHAP",
     "auth_username": "XXX",
     "auth_password": "XXX",
     "target_iqn": "iqn.2010-10.com.example:vol-X",
     "target_portal": "192.168.0.123:3260",
     "volume_id": "12345678-...",
     "target_lun": 0,
     "access_mode": "rw",
     "target_discovered": false,
     "encrypted": false,
     "qos_specs": null}
    

    对于 iSCSI 多路径示例

    {"auth_method": "CHAP",
     "auth_username": "XXX",
     "auth_password": "XXX",
     "target_iqns": ["iqn.2010-10.com.example:vol-X",
     "iqn.2010-10.com.example:vol-Y"],
     "target_portals": ["192.168.0.123:3260",
     "192.168.0.124:3260"],
     "volume_id": "12345678-...",
     "target_luns": [0, 1],
     "access_mode": "rw",
     "target_discovered": false,
     "encrypted": false,
     "qos_specs": null}
    

    对于光纤通道示例

    {"device_path": "/dev/disk/by-path/pci-XXXX",
     "encrypted": false,
     "qos_specs": null,
     "target_lun": 1,
     "access_mode": "rw",
     "target_wwn": ["XXXX"]}
    

    REST API 会屏蔽凭据信息,例如 iSCSI 和 iSCSI 多路径示例中的 auth_usernameauth_password,以避免安全风险。

  • 添加 REST API 端点以获取/设置它们的值。有关详细信息,请参阅 REST API 影响部分。

    • /v1/volume/connectors

    • /v1/volume/targets

    • /v1/nodes/<node_uuid 或 name>/volume/connectors

    • /v1/nodes/<node_uuid 或 name>/volume/targets

  • node.properties['capabilities'] 中添加新的功能标志。这些标志显示节点是否可以从每个后端的卷启动。如果它可以从卷启动,则应将该标志设置为 true。

    • iscsi_boot

    • fibre_channel_boot

    注意

    如果裸机节点支持从特定卷启动,则应将其设置为 true。它可以通过操作员手动填充或通过检查填充,但这不在本规范的范围内。

    注意

    将来,Ironic 将提供驱动程序功能信息。Nova 可以使用这些信息来选择合适的节点。

  • 如果指定了 target 列表,则由处理部署的驱动程序负责处理此问题。对于多路径,Ironic 驱动程序、裸机硬件和操作系统应支持它。如果 Ironic 驱动程序和裸机硬件支持它,但实例操作系统不理解它,则可能导致实例启动失败或损坏 Cinder 卷中的信息。

  • 存储在 volume_connector 和 volume_target 表中的信息用于驱动程序以从卷启动节点。有关参考驱动程序、驱动程序接口的更改,请参阅规范 [4]

备选方案

  • 将连接器信息保存到新的节点属性,例如 volume_initiator_info。此更改对当前代码和 API 的影响较小,但建议的更改具有更多好处,例如更好的完整性检查、更快的数据库查询以及更易于存储与特定连接器相关的信息。

  • 将 target 信息保存到新的节点属性,例如 volume_target_info。此更改对当前代码和 API 的影响较小,但建议的更改具有更多好处,例如更好的完整性检查、更快的数据库查询以及更易于存储与特定 target 相关的信息。

  • 将 target 信息保存到 instance_info 以及其他与实例相关的信息。这似乎很简单,因为基本上 target 卷信息与实例相关。在这种情况下,node.instance_info 被嵌套以存储 target 信息。这使得用户难以操作 target 信息,并且驱动程序难以验证它。另一方面,当前方法可以避免嵌套 instance_info,因此更容易使用这些信息。请注意,ironic 在节点 tear_down 时会清除 target 连接信息。

  • 不实现 target 和发起者信息的存储,最终不会改善用户体验,并且需要手动进行部署后配置才能进行带外控制。对于带内使用,Nova ironic 驱动程序可以管理发起者信息,jroll 提出建议 [2]

数据模型影响

  • 在 objects/volume_connector.py 中添加新的对象类型 VolumeConnector。它继承自 IronicObject 类。新的对象将具有以下字段

    • id

    • uuid

    • node_id

    • type

    • connector_id

    • extra

    • created_at(在 IronicObject 类中定义)

    • updated_at(在 IronicObject 类中定义)

  • 在 objects/volume_target.py 中添加新的对象类型 VolumeTarget。它继承自 IronicObject 类。新的对象将具有以下字段

    • id

    • uuid

    • node_id

    • volume_type

    • properties

    • boot_index

    • volume_id

    • extra

    • created_at(在 IronicObject 类中定义)

    • updated_at(在 IronicObject 类中定义)

状态机影响

无。

REST API 影响

将引入六个新的 REST API 端点。

  • /v1/volume/connectors

    • 要设置卷连接器(发起者)信息

      POST /v1/volume/connectors
      

      使用包含卷连接器 JSON 描述的主体。如果某些必需的属性缺失或具有无效值,则成功时返回 201,如果存在相同的卷连接器,则返回 409。

    • 获取所有卷连接器信息

      GET /v1/volume/connectors
      

      此操作将返回一个字典列表。它包含有关所有卷连接器信息

      {
          "volume_connectors":[
              {
                  "connector_id": "<wwpn>",
                  "links": [ ... ],
                  "type": "wwpn",
                  "uuid": "<uuid>",
              },
              {
                  "connector_id": "<wwpn>",
                  "links": [ ... ],
                  ...
              },
              ...
          ]
      }
      

      成功时返回 200

      此操作可以接受参数,例如 typecontainer_idlimitmarkersort_dirfields

    • 获取所有卷连接器的详细信息

      GET /v1/volume/connectors/detail
      

      该操作将返回一个字典列表。它包含有关所有卷连接器的详细信息

      {
          "volume_connectors":[
              {
                  "connector_id": "<wwpn>",
                  "created_at": "<created_date>",
                  "extra": {},
                  "links": [ ... ],
                  "node_uuid": "<node_uuid>",
                  "type": "wwpn",
                  "updated_at": "<updated_date>",
                  "uuid": "<uuid>",
              },
              {
                  "connector_id": "<wwpn>",
                  "created_at": "<created_date>",
                  ...
              },
              ...
          ]
      }
      

      成功时返回 200。

      此操作可以接受参数,例如 typecontainer_idlimitmarkersort_dir

    • 可以将 node 作为参数传递,该参数可以是节点名称或节点 UUID,以获取该特定节点的所有卷连接器

      GET /v1/volume/connectors?node=<node_uuid or name>
      GET /v1/volume/connectors/detail?node=<node_uuid or name>
      

      成功时返回 200,如果未找到节点则返回 404。

  • /v1/volume/connectors/<volume_connector_uuid>

    • 获取特定卷连接器的详细信息

      GET /v1/volume/connectors/<volume_connector_uuid>
      

      成功时返回 200,如果未找到卷连接器则返回 404。

    • 更新特定的卷连接器

      PATCH /v1/volume/connectors/<volume_connector_uuid>
      

      成功时返回 200 和更新资源的表示形式,如果未找到卷连接器则返回 404。

    注意

    当节点处于 POWER_ON 或 REBOOT 状态时,阻止更新连接器信息。这意味着用户必须确保节点处于 POWER_OFF 状态才能更新连接器信息。更新连接器信息后,驱动程序应更新节点配置。

    • 删除卷连接器

      DELETE /v1/volume/connectors/<volume_connector_uuid>
      

      成功时返回 204,如果未找到卷连接器则返回 404,如果节点未处于 POWER_OFF 状态则返回 400。

  • /v1/nodes/<node_uuid name>/volume/connectors

    • 获取节点的全部卷连接器信息

      GET ``/v1/nodes/<node_uuid or name>/volume/connectors``
      
  • /v1/volume/targets

    • 设置卷 target 信息

      POST /v1/volume/targets
      

      使用包含卷 target JSON 描述的主体。如果某些必需的属性缺失或具有无效值,则成功时返回 201,如果存在相同的卷 target,则返回 409。

    • 获取所有卷 target 信息

      GET /v1/volume/targets
      

      此操作将返回一个字典列表。它包含有关所有卷 target 信息

      {
          "volume_targets":[
              {
                  "boot_index", "<boot_index>",
                  "links": [ ... ],
                  "uuid": "<uuid>",
                  "volume_id": "<volume_id>"
                  "volume_type": "<volume_target_type>",
              },
              {
                  "boot_index", "<boot_index>",
                  "links": [ ... ],
                  ...
              },
              ...
          ]
      }
      

      成功时返回 200。

      此操作可以接受参数,例如 boot_indexvolume_idvolume_typelimitmarkersort_dirfields

    • 获取所有卷 target 的详细信息

      GET /v1/volume/targets/detail
      

      该操作将返回一个字典列表。它包含有关所有卷 target 的详细信息

      {
          "volume_targets":[
              {
                  "boot_index": "<boot_index>",
                  "created_at": "<created_date>",
                  "extra": {},
                  "links": [ ... ],
                  "node_uuid": "<node_uuid>",
                  "properties" : { "<target_information>" },
                  "updated_at": "<updated_date>",
                  "uuid": "<uuid>",
                  "volume_id": "<volume_id>",
                  "volume_type": "<volume_target_type>",
              },
              {
                  "boot_index": "<boot_index>",
                  "created_at": "<created_date>",
                  ...
              },
              ...
          ]
      }
      

      成功时返回 200。

      此操作可以接受参数,例如 boot_indexvolume_idvolume_typelimitmarkersort_dir

      注意

      properties 包含凭据信息。此 API 将屏蔽它以避免安全风险。

    • 可以将 node 作为参数传递,该参数可以是节点名称或节点 UUID,以获取该特定节点的所有卷 target

      GET /v1/volume/targets?node=<node_uuid or name>
      GET /v1/volume/targets/detail?node=<node_uuid or name>
      

      成功时返回 200,如果未找到节点则返回 404。

  • /v1/volume/targets/<volume_target_uuid>

    • 获取特定卷 target 的详细信息

      GET /v1/volume/targets/<volume_target_uuid>
      

      成功时返回 200,如果未找到卷 target 则返回 404。

    • 更新特定的卷 target

      PATCH /v1/volume/targets/<volume_target_uuid>
      

      成功时返回 200 和更新资源的表示形式,如果未找到卷 target 则返回 404,如果节点未处于 POWER_OFF 状态则返回 400。

    注意

    当节点处于 POWER_ON 或 REBOOT 状态时,阻止更新 target 信息。这意味着用户必须确保节点处于 POWER_OFF 状态才能更新 target 信息。更新 target 信息后,驱动程序应更新节点配置。

    • 删除卷 target

      DELETE /v1/volume/targets/<volume_target_uuid>
      

      成功时返回 204,如果未找到卷 target 则返回 404,如果节点未处于 POWER_OFF 状态则返回 400。

  • /v1/nodes/<node_uuid name>/volume/targets

    • 获取节点的全部卷 target 信息

      GET ``/v1/nodes/<node_uuid or name>/volume/targets``
      
  • /v1/nodes/<node_uuid name>/volume/targets

    • 获取节点的全部卷 target 信息

      GET ``/v1/nodes/<node_uuid or name>/volume/targets``
      

端点 GET /v1/nodes/detail 将提供节点的卷连接器和 target 信息,以及指向它们的链接。此外,端点 GET /v1/nodes/<node_uuid name> 将提供指定节点的卷连接器和 target 信息。

对于上述 REST API 更改,将增加微版本,如果使用较低的微版本访问较新的端点,则将引发 406 错误。

客户端 (CLI) 影响

  • 将向 ironicclient 添加一个新的 VolumeConnectorManager 以获取/设置节点的连接器信息。CLI 也会进行如下修改

    ironic volume-connector-create --node <node> --type <type>
                                   --connector_id <connector_id>
                                   [-e <key=value>] [-u <uuid>]
    ironic volume-connector-delete <uuid> [<uuid>]
    ironic volume-connector-list [--detail] [--type <type>]
                                 [--connector_id <connector_id>]
                                 [--limit <limit>] [--marker <uuid>]
                                 [--sort-key <field>] [--sort-dir <direction>]
                                 [--fields <field> [<field> ...]]
    ironic volume-connector-show [--fields <field> [<field> ...]] <uuid>
    ironic volume-connector-update <uuid> <op> <path=value> [<path=value> ...]
    
    ironic node-volume-connector-list [--detail] [--limit <limit>]
                                      [--marker <uuid>] [--sort-key <field>]
                                      [--sort-dir <direction>]
                                      [--fields <field> [<field> ...]]
                                      <node>
    
  • 将向 ironicclient 添加一个新的 VolumeTargetManager 以获取/设置节点的 target 信息。CLI 也会进行如下修改

    ironic volume-target-create --node <node> --volume_type <volume_type>
                                --volume_id <volume_id>
                                [--properties <key=value>]
                                [--boot_index <boot_index>]
                                [-e <key=value>] [-u <uuid>]
    ironic volume-target-delete <uuid> [<uuid>]
    ironic volume-target-list [--detail] [--volume_type <volume_type>]
                              [--volume_id <volume_id>]
                              [--boot_index <boot_index>] [--limit <limit>]
                              [--marker <uuid>] [--sort-key <field>]
                              [--sort-dir <direction>]
                              [--fields <field> [<field> ...]]
    ironic volume-target-show [--fields <field> [<field> ...]] <uuid>
    ironic volume-target-update <uuid> <op> <path=value> [<path=value> ...]
    
    ironic node-volume-target-list [--detail] [--limit <limit>]
                                   [--marker <uuid>] [--sort-key <field>]
                                   [--sort-dir <direction>]
                                   <node>
    
  • 新的对象,CreateBaremetalVolumeConnectorDeleteBaremetalVolumeConnectorListBaremetalVolumeConnectorSetBaremetalVolumeConnectorShowBaremetalVolumeConnectorUnsetBaremetalVolumeConnector 将被添加到 openstackclient 插件中,以获取/设置节点的连接器信息。 此外,CLI 将被修改如下

    openstack baremetal volume connector create [-h]
                                                [-f {json,shell,table,value,yaml}]
                                                [-c COLUMN]
                                                [--max-width <integer>]
                                                [--noindent] [--prefix PREFIX]
                                                --node <node_uuid> --type <type>
                                                --connector_id <connector_id>
                                                [--extra <key=value>]
                                                [--uuid <uuid>]
    openstack baremetal volume connector delete [-h] <connector> [<connector>]
    openstack baremetal volume connector list [-h]
                                              [-f {json,shell,table,value,yaml}]
                                              [-c COLUMN]
                                              [--max-width <integer>]
                                              [--noindent]
                                              [--quote {all,minimal,none,nonnumeric}]
                                              [--limit <limit>]
                                              [--marker <uuid>]
                                              [--sort <key>[:<direction>]]
                                              [--long | fields <field [field] ...>]
    openstack baremetal volume connector set [-h] [--node <node>]
                                             [--type <type>]
                                             [--connector_id <connector_id>]
                                             [--extra <key=value>] <connector>
    openstack baremetal volume connector show [-h]
                                              [-f {json,shell,table,value,yaml}]
                                              [-c COLUMN]
                                              [--max-width <integer>]
                                              [--noindent] [--prefix PREFIX]
                                              [--fields <field> [<field> ...]]
                                              <connector>
    openstack baremetal volume connector unset [-h] [--extra <key>] <connector>
    
  • 新的对象,CreateBaremetalVolumeTargetDeleteBaremetalVolumeTargetListBaremetalVolumeTargetSetBaremetalVolumeTargetShowBaremetalVolumeTargetUnsetBaremetalVolumeTarget 将被添加到 openstackclient 插件中,以获取/设置节点的target信息。 此外,CLI 将被修改如下

    openstack baremetal volume target create [-h]
                                             [-f {json,shell,table,value,yaml}]
                                             [-c COLUMN] [--max-width <integer>]
                                             [--noindent] [--prefix PREFIX]
                                             --node <node_uuid> --type <type>
                                             --volume_id <volume_id>
                                             [--properties <key=value>]
                                             [--boot_index <boot_index>]
                                             [--extra <key=value>]
                                             [--uuid <uuid>]
    openstack baremetal volume target delete [-h] <target> [<target>]
    openstack baremetal volume target list [-h]
                                           [-f {json,shell,table,value,yaml}]
                                           [-c COLUMN] [--max-width <integer>]
                                           [--noindent]
                                           [--quote {all,minimal,none,nonnumeric}]
                                           [--limit <limit>] [--marker <uuid>]
                                           [--sort <key>[:<direction>]]
                                           [--long | fields <field [field] ...>]
    openstack baremetal volume target set [-h] [--node <node>] [--type <type>]
                                          [--volume_id <volume_id>]
                                          [--properties <key=value>]
                                          [--boot_index <boot_index>]
                                          [--extra <key=value>] <target>
    openstack baremetal volume target show [-h]
                                           [-f {json,shell,table,value,yaml}]
                                           [-c COLUMN] [--max-width <integer>]
                                           [--noindent] [--prefix PREFIX]
                                           [--fields <field> [<field> ...]]
                                           <target>
    openstack baremetal volume target unset [-h]
                                            [--properties <key>]
                                            [--boot_index] [--extra <key>]
                                            <target>
    

RPC API 影响

四个新的 rpcapi 方法 update_volume_connectordestroy_volume_connectorupdate_volume_targetdestroy_volume_target 将被添加。

  • update_volume_connector

    此方法接收 context 和 volume connector 对象作为输入,并返回更新后的 volume connector 对象。

  • destroy_volume_connector

    此方法接收 context 和 volume connector 对象作为输入。

  • update_volume_target

    此方法接收 context 和 volume target 对象作为输入,并返回更新后的 volume target 对象。

  • destroy_volume_target

    此方法接收 context 和 volume target 对象作为输入。

驱动程序 API 影响

无。

Nova 驱动程序影响

在生成新的实例时,Nova Ironic virt 驱动程序会查询 Ironic(通过 API)以查找 volume connector 信息。 它将 volume connector 信息传递给 Cinder,Cinder 返回 target 信息。 然后将其传递给 Ironic。 Nova Ironic 驱动程序的详细信息可以在规范 [5] 中找到。

Ramdisk 影响

安全影响

无。

注意

对于 FC zoning,Cinder 会负责处理 [6]

其他最终用户影响

无。

可扩展性影响

无。

性能影响

这可能会延长 nova boot/delete 所需的时间,但影响不大,并且对企业用户来说很重要。

其他部署者影响

  • 如果管理员想要提供从 volume 启动功能,他们需要在激活节点之前填写以下 initiator 信息。

    • iSCSI

      • ip

      • iqn

      • mac

        注意

        当使用 Neutron 管理存储网络时,可以省略 ip。

    • FC

      • wwnn

      • wwpn

    管理员需要设置 node.properties[‘capabilities’] (iscsi_boot 和/或 fibre_channel_boot) 为 true。

    如果可以自动收集并注册这些信息,则会更好。 例如,对于具有 FC-HBA 的节点,inspection(带内)可以从 sysfs 获取 wwnn 和 wwpn,如下所示

    # cat /sys/class/scsi_host/host*/device/fc_host/host*/node_name
    # cat /sys/class/scsi_host/host*/device/fc_host/host*/port_name
    
  • 如果用户想在 Ironic 独立模式下从 volume 启动节点,他们需要额外的工具来利用此功能。 例如,该工具需要执行以下操作:

    • 从 Ironic 获取 initiator 信息

    • 使用 initiator 信息调用存储管理工具以创建新的 volume(可能从模板)并将其附加到 initiator

    • 从存储管理工具获取 target 信息

    • 将 target 信息放入 Ironic

开发人员影响

驱动程序开发人员可以利用上述信息来编写驱动程序中的从 volume 启动支持。 参考驱动程序和驱动程序接口规范的详细信息在 [4] 中描述。

实现

负责人

主要负责人

satoru-moriya-br

其他贡献者

rameshg87

工作项

  • 创建名为 volume_connectors 和 volume_targets 的新表

  • 创建新的 DB API 方法

  • 创建名为 VolumeConnector 和 VolumeTarget 的新 Object

  • 创建新的 RPC API 方法

  • 创建新的 REST API 端点

  • 记录更改

  • 增强 inspector 以注册可用的连接器信息

  • 增强 Client(CLI) 以获取/设置连接器和 target 信息

  • 增强 Nova-Ironic 驱动程序以支持使用这些 API 从 volume 启动

依赖项

测试

  • 将添加/更新单元测试以覆盖这些更改。

  • Tempest 测试将被添加到 Ironic 中,以确保以下新添加的 API 端点正常工作。

升级和向后兼容性

添加数据库的迁移脚本。

文档影响

文档,例如安装指南和 api-ref 将被更新,以解释新添加的字段和端点。

参考资料