| ======= |
| 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. |
| |
| Prerequisites |
| ============= |
| |
| 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. |
| |
| Plugin Interface |
| ================ |
| |
| 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/foo, then the name here should be "foo") :: |
| |
| define_plugin <YOUR PLUGIN> |
| |
| If your plugin depends on another plugin, indicate it in this file |
| with one or more lines like the following:: |
| |
| plugin_requires <YOUR PLUGIN> <OTHER PLUGIN> |
| |
| For a complete example, if the plugin "foo" depends on "bar", the |
| ``settings`` file should include:: |
| |
| define_plugin foo |
| plugin_requires foo bar |
| |
| 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``. |
| |
| They are added in the following format:: |
| |
| [[local|localrc]] |
| enable_plugin <NAME> <GITURL> [GITREF] |
| |
| - ``name`` - an arbitrary name. (ex: glusterfs, docker, zaqar, congress) |
| - ``giturl`` - a valid git url that can be cloned |
| - ``gitref`` - an optional git ref (branch / ref / tag) that will be |
| cloned. Defaults to master. |
| |
| An example would be as follows:: |
| |
| enable_plugin ec2-api https://git.openstack.org/openstack/ec2-api |
| |
| plugin.sh contract |
| ================== |
| |
| ``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. |
| |
| Example plugin |
| ==================== |
| |
| An example plugin would look something as follows. |
| |
| ``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 |
| |
| Plugin Execution Order |
| ====================== |
| |
| 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. |
| |
| System Packages |
| =============== |
| |
| 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. |
| |
| - ``./devstack/files/rpms/$plugin_name`` - Packages to install when running |
| on Red Hat, Fedora, CentOS or XenServer. |
| |
| - ``./devstack/files/rpms-suse/$plugin_name`` - Packages to install when |
| running on SUSE Linux or openSUSE. |
| |
| |
| Using Plugins in the OpenStack Gate |
| =================================== |
| |
| 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 |
| <http://git.openstack.org/cgit/openstack-infra/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. |
| 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://git.openstack.org/openstack/ec2-api" |
| |
| See Also |
| ======== |
| |
| For additional inspiration on devstack plugins you can check out the |
| `Plugin Registry <plugin-registry.html>`_. |
| |
| .. _service types authority: https://specs.openstack.org/openstack/service-types-authority/ |