Plugins

The OpenStack ecosystem is wide and deep, and only growing more so
every day. The value of DevStack is that it’s simple enough to
understand what it’s doing clearly. And yet we’d like to support as
much of the OpenStack Ecosystem as possible. We do that with plugins.

DevStack plugins are bits of bash code that live outside the DevStack
tree. They are called through a strong contract, so these plugins can
be sure that they will continue to work in the future as DevStack
evolves.

If you are planning to create a plugin that is going to host a service in the
service catalog (that is, your plugin will use the command
get_or_create_service) please make sure that you apply to the service
types authority to reserve a valid service-type. This will help to make sure
that all deployments of your service use the same service-type.

DevStack supports a standard mechanism for including plugins from
external repositories. The plugin interface assumes the following:

An external git repository that includes a devstack/ top level
directory. Inside this directory there can be 3 files.

override-defaults - a file containing global variables that
will be sourced before the lib/* files. This allows the plugin
to override the defaults that are otherwise set in the lib/*
files.

For example, override-defaults may export CINDER_ENABLED_BACKENDS
to include the plugin-specific storage backend and thus be able
to override the default lvm only storage backend for Cinder.

settings - a file containing global variables that will be
sourced very early in the process. This is helpful if other plugins
might depend on this one, and need access to global variables to do
their work.

Your settings should include any enable_service lines required
by your plugin. This is especially important if you are kicking off
services using run_process as it only works with enabled
services.

Be careful to allow users to override global-variables for
customizing their environment. Usually it is best to provide a
default value only if the variable is unset or empty; e.g. in bash
syntax FOO=${FOO:-default}.

The file should include a define_plugin line to indicate the
plugin’s name, which is the name that should be used by users on
“enable_plugin” lines. It should generally be the last component of
the git repo path (e.g., if the plugin’s repo is
openstack/devstack-foo, then the name here should be “foo”)

define_plugin<YOURPLUGIN>

If your plugin depends on another plugin, indicate it in this file
with one or more lines like the following:

plugin_requires<YOURPLUGIN><OTHERPLUGIN>

For a complete example, if the plugin “foo” depends on “bar”, the
settings file should include:

define_pluginfooplugin_requiresfoobar

Devstack does not currently use this dependency information, so it’s
important that users continue to add enable_plugin lines in the
correct order in local.conf, however adding this information
allows other tools to consider dependency information when
automatically generating local.conf files.

plugin.sh - the actual plugin. It is executed by devstack at
well defined points during a stack.sh run. The plugin.sh
internal structure is discussed below.

Plugins are registered by adding the following to the localrc section
of local.conf.

plugin.sh is a bash script that will be called at specific points
during stack.sh, unstack.sh, and clean.sh. It will be
called in the following way:

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

mode can be thought of as the major mode being called, currently
one of: stack, unstack, clean. phase is used by modes
which have multiple points during their run where it’s necessary to
be able to execute code. All existing mode and phase points
are considered strong contracts and won’t be removed without a
reasonable deprecation period. Additional new mode or phase
points may be added at any time if we discover we need them to support
additional kinds of plugins in devstack.

The current full list of mode and phase are:

stack - Called by stack.sh four times for different phases
of its run:

pre-install - Called after system (OS) setup is complete and
before project source is installed.

install - Called after the layer 1 and 2 projects source and
their dependencies have been installed.

post-config - Called after the layer 1 and 2 services have
been configured. All configuration files for enabled services
should exist at this point.

extra - Called near the end after layer 1 and 2 services have
been started.

test-config - Called at the end of devstack used to configure tempest
or any other test environments

unstack - Called by unstack.sh before other services are shut
down.

clean - Called by clean.sh before other services are cleaned,
but after unstack.sh has been called.

# plugin.sh - DevStack plugin.sh dispatch script templatefunctioninstall_template{...}functioninit_template{...}functionconfigure_template{...}# check for service enabledifis_service_enabledtemplate;thenif[["$1"=="stack"&&"$2"=="pre-install"]];then# Set up system servicesecho_summary"Configuring system services Template"install_packagecowsayelif[["$1"=="stack"&&"$2"=="install"]];then# Perform installation of service sourceecho_summary"Installing Template"install_templateelif[["$1"=="stack"&&"$2"=="post-config"]];then# Configure after the other layer 1 and 2 services have been configuredecho_summary"Configuring Template"configure_templateelif[["$1"=="stack"&&"$2"=="extra"]];then# Initialize and start the template serviceecho_summary"Initializing Template"init_templatefiif[["$1"=="unstack"]];then# Shut down template services# no-op:fiif[["$1"=="clean"]];then# Remove state and transient data# Remember clean.sh first calls unstack.sh# no-op:fifi

Plugins are run after in tree services at each of the stages
above. For example, if you need something to happen before Keystone
starts, you should do that at the post-config phase.

Multiple plugins can be specified in your local.conf. When that
happens the plugins will be executed in order at each phase. This
allows plugins to conceptually depend on each other through
documenting to the user the order they must be declared. A formal
dependency mechanism is beyond the scope of the current work.

Devstack provides a framework for getting packages installed at an early
phase of its execution. These packages may be defined in a plugin as files
that contain new-line separated lists of packages required by the plugin

Supported packaging systems include apt and yum across multiple distributions.
To enable a plugin to hook into this and install package dependencies, packages
may be listed at the following locations in the top-level of the plugin
repository:

./devstack/files/debs/$plugin_name - Packages to install when running
on Ubuntu, Debian or Linux Mint.

For everyday use, DevStack plugins can exist in any git tree that’s
accessible on the internet. However, when using DevStack plugins in
the OpenStack gate, they must live in projects in OpenStack’s
gerrit. This allows testing of the plugin as well as provides network
isolation against upstream git repository failures (which we see often
enough to be an issue).

Ideally a plugin will be included within the devstack directory of
the project they are being tested. For example, the openstack/ec2-api
project has its plugin support in its own tree.

However, some times a DevStack plugin might be used solely to
configure a backend service that will be used by the rest of
OpenStack, so there is no “project tree” per say. Good examples
include: integration of back end storage (e.g. ceph or glusterfs),
integration of SDN controllers (e.g. ovn, OpenDayLight), or
integration of alternate RPC systems (e.g. zmq, qpid). In these cases
the best practice is to build a dedicated
openstack/devstack-plugin-FOO project.

To enable a plugin to be used in a gate job, the following lines will
be needed in your jenkins/jobs/<project>.yaml definition in
project-config:

# Because we are testing a non standard project, add the# our project repository. This makes zuul do the right# reference magic for testing changes.exportPROJECTS="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.exportDEVSTACK_LOCAL_CONFIG="enable_plugin ec2-api https://git.openstack.org/openstack/ec2-api"