插件

OpenStack 生态系统非常广泛且不断发展。DevStack 的价值在于它足够简单,可以清楚地理解其运作方式。但同时我们也希望尽可能地支持 OpenStack 生态系统。我们通过插件来实现这一点。

DevStack 插件是位于 DevStack 树之外的 bash 代码片段。它们通过严格的契约进行调用,因此这些插件可以确信,随着 DevStack 的发展,它们将继续工作。

先决条件

如果您计划创建一个将在服务目录中托管服务的插件(也就是说,您的插件将使用 get_or_create_service 命令),请确保您申请 服务类型权限 以保留有效的服务类型。这将有助于确保您服务的部署都使用相同的服务类型。

插件接口

DevStack 支持一种标准机制,用于从外部仓库包含插件。插件接口假定以下内容

一个外部 git 仓库,其中包含一个顶层目录 devstack/。在该目录中可以有 3 个文件。

  • override-defaults - 一个包含全局变量的文件,这些变量将在 lib/* 文件之前被读取。这允许插件覆盖 lib/* 文件中设置的默认值。

    例如,override-defaults 可以导出 CINDER_ENABLED_BACKENDS 以包含插件特定的存储后端,从而能够覆盖 Cinder 的默认仅 lvm 存储后端。

  • settings - 一个包含全局变量的文件,这些变量将在流程的早期被读取。如果其他插件可能依赖于此插件,并且需要访问全局变量来完成其工作,这将很有帮助。

    您的设置应包含插件所需的任何 enable_service 行。如果使用 run_process 启动服务,这一点尤其重要,因为它仅适用于已启用的服务。

    请小心允许用户覆盖全局变量以自定义其环境。通常,最好仅在变量未设置或为空时提供默认值;例如,在 bash 语法中 FOO=${FOO:-default}

    该文件应包含一个 define_plugin 行,以指示插件的名称,该名称是用户在“enable_plugin”行中应使用的名称。通常,它应该是 git 仓库路径的最后一个组件(例如,如果插件的仓库是 openstack/foo,那么这里的名称应该是“foo”)

    define_plugin <YOUR PLUGIN>
    

    如果您的插件依赖于另一个插件,请在此文件中使用如下行指示它

    plugin_requires <YOUR PLUGIN> <OTHER PLUGIN>
    

    对于完整的示例,如果插件“foo”依赖于“bar”,则 settings 文件应包含

    define_plugin foo
    plugin_requires foo bar
    

    Devstack 目前不使用此依赖信息,因此重要的是用户继续在 local.conf 中以正确的顺序添加 enable_plugin 行,但是添加此信息允许其他工具在自动生成 local.conf 文件时考虑依赖信息。

  • plugin.sh - 实际的插件。它由 devstack 在 stack.sh 运行期间定义的特定点执行。插件.sh 的内部结构如下所述。

插件通过将以下内容添加到 local.conf 的 localrc 部分来注册。

它们以以下格式添加

[[local|localrc]]
enable_plugin <NAME> <GITURL> [GITREF]
  • name - 一个任意名称。(例如:glusterfs、docker、zaqar、congress)

  • giturl - 一个有效的 git url,可以克隆

  • gitref - 一个可选的 git ref(分支/ref/tag),将被克隆。默认值为 master。

一个例子如下

enable_plugin ec2-api https://opendev.org/openstack/ec2-api

plugin.sh 契约

plugin.sh 是一个 bash 脚本,它将在 stack.shunstack.shclean.sh 期间的特定点被调用。它将以以下方式被调用

source $PATH/TO/plugin.sh <mode> [phase]

mode 可以被认为是正在调用的主要模式,当前是:stackunstackcleanphase 由具有多个点在其运行期间需要能够执行代码的模式使用。所有现有的 modephase 点都被认为是 强契约,不会在没有合理的弃用期的情况下被删除。如果我们需要它们来支持 devstack 中的其他类型的插件,可以随时添加新的 modephase 点。

当前完整的 modephase 列表是

  • stack - 由 stack.sh 在其运行的不同阶段调用四次

    • pre-install - 在系统(OS)设置完成后,在安装项目源代码之前调用。

    • install - 在安装了第 1 层和第 2 层项目的源代码及其依赖项之后调用。

    • post-config - 在配置了第 1 层和第 2 层服务之后调用。此时应该存在已启用服务的所有配置文件。

    • extra - 在第 1 层和第 2 层服务启动后,在结尾附近调用。

    • test-config - 在 devstack 结尾被调用,用于配置 tempest 或任何其他测试环境

  • unstack - 由 unstack.sh 在关闭其他服务之前调用。

  • clean - 由 clean.sh 在清理其他服务之前调用,但在调用 unstack.sh 之后。

示例插件

一个示例插件如下所示。

devstack/settings:

# settings file for template
enable_service template

devstack/plugin.sh:

# plugin.sh - DevStack plugin.sh dispatch script template

function install_template {
    ...
}

function init_template {
    ...
}

function configure_template {
    ...
}

# check for service enabled
if is_service_enabled template; then

    if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then
        # Set up system services
        echo_summary "Configuring system services Template"
        install_package cowsay

    elif [[ "$1" == "stack" && "$2" == "install" ]]; then
        # Perform installation of service source
        echo_summary "Installing Template"
        install_template

    elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
        # Configure after the other layer 1 and 2 services have been configured
        echo_summary "Configuring Template"
        configure_template

    elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
        # Initialize and start the template service
        echo_summary "Initializing Template"
        init_template
    fi

    if [[ "$1" == "unstack" ]]; then
        # Shut down template services
        # no-op
        :
    fi

    if [[ "$1" == "clean" ]]; then
        # Remove state and transient data
        # Remember clean.sh first calls unstack.sh
        # no-op
        :
    fi
fi

插件执行顺序

插件在上述阶段的树内服务之后运行。例如,如果您需要在 Keystone 启动之前发生某些事情,您应该在 post-config 阶段执行此操作。

可以在您的 local.conf 中指定多个插件。当发生这种情况时,插件将在每个阶段以 顺序 执行。这允许插件通过记录用户必须声明它们的顺序来概念性地相互依赖。正式的依赖机制超出了当前工作的范围。

系统包

基于 Devstack

Devstack 提供了一个自定义框架,用于在执行的早期阶段获取已安装的包。这些包可以定义为插件中的文件,这些文件包含换行符分隔的插件所需的包列表

支持的打包系统包括 apt 和 dnf,跨多个发行版。要启用插件以连接到此并安装软件包依赖项,可以在插件仓库的顶层列出软件包,如下所示

  • ./devstack/files/debs/$plugin_name - 在 Ubuntu 或 Debian 上运行时要安装的软件包。

  • ./devstack/files/rpms/$plugin_name - 在 Red Hat、Fedora 或 CentOS 上运行时要安装的软件包。

虽然没有计划删除这种安装软件包的方法,但插件应将其视为已弃用,以支持下面描述的 bindep 支持。

bindep

bindep 项目已成为 OpenStack 项目指定二进制依赖项的事实标准。

插件可以提供一个 ./devstack/files/bindep.txt 文件,该文件将使用 默认 配置文件安装软件包。有关语法等,请参阅 bindep 文档。

也可以使用 bindep.txt 通过安装函数中提供的 -bindep 标志从源代码安装的项目。例如

if use_library_from_git "diskimage-builder"; then
   GITREPO["diskimage-builder"]=$DISKIMAGE_BUILDER_REPO_URL
   GITDIR["diskimage-builder"]=$DEST/diskimage-builder
   GITBRANCH["diskimage-builder"]=$DISKIMAGE_BUILDER_REPO_REF
   git_clone_by_name "diskimage-builder"
   setup_dev_lib -bindep "diskimage-builder"
fi

将导致 bindep.txtdiskimage-builder 项目所需的任何软件包被安装。但是,在源安装和发布/pypi 安装之间切换项目(例如,使用 foo-dsvmfoo-dsvm-src 测试以涵盖发布依赖项和主版本)的任务将必须处理 bindep.txt 在没有源代码目录的情况下不可用。

在 OpenStack Gate 中使用插件

对于日常使用,DevStack 插件可以存在于互联网上可访问的任何 git 树中。但是,当在 OpenStack gate 中使用 DevStack 插件时,它们必须位于 OpenStack 的 gerrit 中的项目中。这允许测试插件,并提供与上游 git 仓库故障隔离(我们经常看到足以成为问题的故障)。

理想情况下,插件将包含在正在测试的项目的 devstack 目录中。例如,openstack/ec2-api 项目在其自己的树中支持其插件。

但是,有时 DevStack 插件可能仅用于配置将由 OpenStack 的其余部分使用的后端服务,因此没有“项目树”。好的示例包括:集成后端存储(例如 ceph 或 glusterfs)、集成 SDN 控制器(例如 ovn、OpenDayLight)或集成替代 RPC 系统(例如 zmq、qpid)。在这些情况下,最佳实践是构建一个专用的 openstack/devstack-plugin-FOO 项目。

旧版 project-config 作业

要启用插件在 gate 作业中使用,需要在 project-config 中的 jenkins/jobs/<project>.yaml 定义中需要以下行

# Because we are testing a non standard project, add the
# our project repository. This makes zuul do the right
# reference magic for testing changes.
export PROJECTS="openstack/ec2-api $PROJECTS"

# note the actual url here is somewhat irrelevant because it
# caches in nodepool, however make it a valid url for
# documentation purposes.
export DEVSTACK_LOCAL_CONFIG="enable_plugin ec2-api https://opendev.org/openstack/ec2-api"

Zuul v3 作业

请参阅 将 Zuul V2 CI 作业迁移到 V3 中的 devstack_plugins 示例。

参见

有关 devstack 插件的更多灵感,您可以查看 插件注册表