Ironic 中的启动接口

https://blueprints.launchpad.net/ironic/+spec/new-boot-interface

本文档描述了将启动逻辑从当前 Ironic 的“部署”驱动程序中重构到新的启动接口。

问题描述

目前 Ironic 中有一个 DeployInterface。该接口的所有当前实现都负责两件事

  • 启动裸机节点 - 包括部署 ramdisk 和部署的实例。

  • 在裸机节点上实际部署镜像。

这两者是独立的函数,因此应该分别抽象出来。这使得可以轻松地将各种启动机制(如 PXE、iPXE、虚拟介质)与各种部署机制(iscsi 部署、agent 部署以及其他可能的部署机制,例如通过 torrent、multicast 部署,相关讨论已经开始)混合和匹配,而无需重复代码。

提议的变更

  • 需要添加一个新的 BootInterface。该接口将建议实现者采用以下方法

    @six.add_metaclass(abc.ABCMeta)
    class BootInterface(object):
      """Interface for inspection-related actions."""
    
      @abc.abstractmethod
      def get_properties(self):
          """Return the properties of the interface.
    
          :returns: dictionary of <property name>:<property description>
         """
    
      @abc.abstractmethod
      def validate(self, task):
          """Validate the driver-specific info for booting.
    
          This method validates the driver-specific info for booting a ramdisk
          and an instance on the node.  If invalid, raises an exception;
          otherwise returns None.
    
          :param task: a task from TaskManager.
          :raises: InvalidParameterValue
          :raises: MissingParameterValue
          """
    
      @abc.abstractmethod
      def prepare_ramdisk(self, task, ramdisk_params):
          """Prepares the boot of Ironic ramdisk.
    
          This method prepares the boot of the deploy ramdisk after reading
          relevant information from the node's database.
    
          :param task: a task from TaskManager.
          :param ramdisk_params: the options to be passed to the ironic ramdisk.
              Different interfaces might want to boot the ramdisk in different
              ways by passing parameters to them.  For example,
              * When DIB ramdisk is booted to deploy a node, it takes the
                parameters iscsi_target_iqn, deployment_id, ironic_api_url, etc.
              * When Agent ramdisk is booted to deploy a node, it takes the
                parameters ipa-driver-name, ipa-api-url, root_device, etc.
              Other interfaces can make use of ramdisk_params to pass such
              information.  Different implementations of boot interface will
              have different ways of passing parameters to the ramdisk.
          """
    
      @abc.abstractmethod
      def clean_up_ramdisk(self, task):
          """Tears down the boot of Ironic ramdisk.
    
          This method tears down the boot of the deploy ramdisk after reading
          relevant information from the node's database.
    
          :param task: a task from TaskManager.
          """
    
      @abc.abstractmethod
      def prepare_instance(self, task):
          """Prepares the boot of instance.
    
          This method prepares the boot of the instance after reading
          relevant information from the node's database.
    
          :param task: a task from TaskManager.
          """
    
      @abc.abstractmethod
      def clean_up_instance(self, task):
          """Tears down the boot of instance.
    
          This method tears down the boot of the instance after reading
          relevant information from the node's database.
    
          :param task: a task from TaskManager.
          """
    
  • 将创建以下新的 BootInterface 实现。

    • pxe.PXEBoot - 使用 PXE 启动裸机节点

    • ipxe.IPXEBoot - 使用 iPXE 启动裸机节点

    • ilo.boot.IloVirtualMediaBoot - 使用 iLO 虚拟介质启动裸机节点。

    注意

    虽然 IPXEBoot 和 PXEBoot 目前在同一个部署驱动程序中,但准备裸机以从 PXE 和 iPXE 启动的步骤是不同的(即使它们共享一些通用代码)。我们将把它们重构为单独的启动接口。Kilo 行为,即同时只使用 PXE 或 iPXE,将保留 - 驱动程序将根据 CONF.pxe.ipxe_enabled 实例化 pxe.PXEBoot 或 ipxe.IPXEBoot。

  • 上述 BootInterface 实现的代码将从 pxe.PXEDeployagent.AgentDeployilo.IloVirtualMediaIscsiDeployilo.IloVirtualMediaAgentDeploy 中获取。这些 DeployInterface 实现将不再包含任何与裸机节点启动相关的逻辑。

  • pxe.PXEDeploy 将被重构为 pxe.PXEBootiscsi_deploy.ISCSIDeploy

  • 每个驱动程序将说明它希望实例化哪个 BootInterface 实现。例如,pxe_ipmitool 驱动程序将如下所示

    class PXEAndIPMIToolDriver(base.BaseDriver):
      """PXE + IPMITool driver"""
    
      def __init__(self):
        self.power = ipmitool.IPMIPower()
        self.console = ipmitool.IPMIShellinaboxConsole()
        self.boot = pxe.PXEBoot()
        self.deploy = iscsi_deploy.ISCSIDeploy()
        self.management = ipmitool.IPMIManagement()
        self.vendor = pxe.VendorPassthru()
        self.inspect = discoverd.DiscoverdInspect.create_if_enabled(
            'PXEAndIPMIToolDriver')
    

注意

在实现后,将启动接口也包含在驱动程序名称中可能是有意义的。因此,这需要一个更周全的过程来重命名驱动程序,解决向后兼容性问题等。因此,这不在本文档的范围内。可以在实现后稍后解决。

备选方案

我们可以继续将启动和部署逻辑放在一起,但这会导致代码重复和不必要的重构,尤其是在未来添加额外的部署机制和启动机制时。

数据模型影响

无。

状态机影响

无。

REST API 影响

无。

RPC API 影响

无。

客户端 (CLI) 影响

无。

驱动程序 API 影响

这添加了一个新的 BootInterface(如上所述),驱动程序编写者可以使用它与部署驱动程序一起使用。BootInterface 不是一个强制接口。

Nova 驱动程序影响

无。

安全影响

无。

其他最终用户影响

无。

可扩展性影响

无。

性能影响

无。

其他部署者影响

无。

开发人员影响

鼓励在 Ironic 中添加新部署机制的新驱动程序开发人员将启动和部署逻辑分离,以便可以轻松地重用它。

实现

负责人

rameshg87

工作项

  • 添加新的启动接口

  • 创建 pxe.PXEBootipxe.IPXEBoot 并重构 pxe.PXEDeployiscsi_deploy.ISCSIDeploy 以使用这些启动接口。

  • 重构 agent.AgentDeploy 以使用新的 pxe.PXEBootipxe.IPXEBoot(是的,我们正在为 agent 部署添加 iPXE 支持)。

  • 创建 ilo.boot.IloVirtualMediaBoot,并重构 IloVirtualMediaIscsiDriverIloVirtualMediaAgentDriver 以使用新的启动接口。

依赖项

无。

测试

将更新新接口的单元测试。由于此更改没有添加任何新功能,因此当前的 upstream CI 测试应该足够。

升级和向后兼容性

这不会破坏树外的部署驱动程序。仍然可以实现用于配置裸机节点的部署驱动程序,而无需启动接口 - 即无需单独的启动和部署接口。这是因为 conductor 仍然将使用所有已发布的 DeployInterface 接口来部署裸机节点。

此更改提出了一个新的可选启动接口的添加,该接口可以用作 DeployInterface 的辅助工具,并重构所有 upstream 部署驱动程序以遵循此逻辑。

文档影响

现有接口的更改将记录在案。此外,新的开发人员文档将更新,以鼓励将部署逻辑拆分为单独的启动和部署接口。

参考资料

不符合本文档的要求,但一个 POC 演示其外观:* https://review.openstack.org/#/c/166512/ * https://review.openstack.org/#/c/166513/ * https://review.openstack.org/#/c/166521/