为 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(length=36)

      • UniqueConstraint

    • node_id

      • Integer

      • ForeignKeyConstraint(‘nodes.id’)

    • created_at

      • DateTime

    • updated_at

      • DateTime

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

      • String(Length=32)

      • UniqueConstraint(type, connector_id)

    • connector_id

      • String(length=255)

      • UniqueConstraint(type, connector_id)

    • extra

      • Text

    注意

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

    注意

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

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

    • id

      • Integer

      • PrimaryKeyConstraint

    • uuid

      • String(length=36)

      • UniqueConstraint

    • node_id

      • Integer

      • ForeignKeyConstraint(‘nodes.id’)

    • created_at

      • DateTime

    • updated_at

      • DateTime

    • volume_type

      • String(length=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(length=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,如果缺少某些必需的属性或具有无效值,或者如果同一卷连接器已存在条目,则返回 400 或 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 已存在条目,则返回 400 或 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>
    
  • 一个新的 VolumeTargetManager 将被添加到 ironicclient 中,用于获取/设置节点的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>
    
  • 新的对象,CreateBaremetalVolumeConnector, DeleteBaremetalVolumeConnector, ListBaremetalVolumeConnector, SetBaremetalVolumeConnector, ShowBaremetalVolumeConnector, 和 UnsetBaremetalVolumeConnector 将被添加到 openstackclient 插件中,用于获取/设置节点的connector信息。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>
    
  • 新的对象,CreateBaremetalVolumeTarget, DeleteBaremetalVolumeTarget, ListBaremetalVolumeTarget, SetBaremetalVolumeTarget, ShowBaremetalVolumeTarget, 和 UnsetBaremetalVolumeTarget 将被添加到 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_connector, destroy_volume_connector, update_volume_target, 和 destroy_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所需的时间,但影响不大,并且对企业用户来说很重要。

其他部署者影响

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

    • iSCSI

      • ip

      • iqn

      • mac

        注意

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

    • FC

      • wwnn

      • wwpn

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

    如果inspection能够自动收集并注册这些信息,会更好。例如,对于具有FC-HBA的节点,inspection(in-band)可以从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

开发人员影响

驱动程序开发者可以利用上述信息编写boot from volume支持到他们的驱动程序中。关于参考驱动程序和驱动程序接口规范的详细信息在[4]中描述。

实现

负责人

主要负责人

satoru-moriya-br

其他贡献者

rameshg87

工作项

  • 创建名为volume_connectors和volume_targets的新表

  • 创建新的DB API方法

  • 创建名为VolumeConnector和VolumeTarget的新Object

  • 创建新的RPC API方法

  • 创建新的REST API端点

  • 记录这些更改

  • 增强inspector以注册可用的connector信息

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

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

依赖项

测试

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

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

升级和向后兼容性

添加数据库迁移脚本。

文档影响

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

参考资料