为 Ironic 节点添加卷连接信息¶
https://bugs.launchpad.net/ironic/+bug/1526231
此 RFE 引入 Ironic 中的更改,以支持连接和从远程卷启动实例。
问题描述¶
当用户使用 Cinder 卷启动裸机实例时,Nova 会协调与 Cinder 和 Ironic 的通信。启动过程的工作流程如下:
(准备) 管理员使用发起者信息注册节点。
用户要求 Cinder 创建启动卷。
用户要求 Nova 从 Cinder 卷启动节点。
Nova 调用 Ironic 以收集 iSCSI/FC 发起者信息。Ironic 收集发起者信息并将其返回给 Nova。
Nova 调用 Cinder 将卷连接到节点。Cinder 将卷连接到节点并返回连接信息,其中包括目标信息。
Nova 将节点的 target 信息传递给 Ironic
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 所做的那样。
注意
properties和extra在数据库中是文本,但在对象中是一个字典(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_username和auth_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 类。新的对象将具有以下字段:iduuidnode_idtypeconnector_idextracreated_at(在 IronicObject 类中定义)updated_at(在 IronicObject 类中定义)
在 objects/volume_target.py 中添加新的对象类型
VolumeTarget。它继承自 IronicObject 类。新的对象将具有以下字段:iduuidnode_idvolume_typepropertiesboot_indexvolume_idextracreated_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。
此操作可以接受参数,例如
type、container_id、limit、marker、sort_dir和fields。获取所有卷连接器的详细信息
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。
此操作可以接受参数,例如
type、container_id、limit、marker和sort_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_index、volume_id、volume_type、limit、marker、sort_dir和fields。获取所有卷 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_index、volume_id、volume_type、limit、marker和sort_dir。注意
properties 包含凭据信息。此 API 将屏蔽它以避免安全风险。
可以将
node作为参数传递,该参数可以是节点名称或节点 UUID,以获取该特定节点的所有卷 targetGET /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,将被更新以解释新添加的字段和端点。