Accessing NFS from OpenStack: How to build a Fuel plugin

OpenStack is constantly evolving, with much of that innovation from individuals or groups that add capabilities for specific tasks. Developing a plugin for OpenStack is a great way to incorporate new functionality, and enabling the plugin with Fuel simplifies both deployment and management.

Fuel is the tool for deploying and managing Mirantis OpenStack, which you can also use to deploy and manage the plugins you create. In this article we show you how to create an NFS plugin for Fuel, test it, release it, and if desired, submit it for Mirantis certification. You can deploy both certified and noncertified plugins with Fuel.

Before we get into developing an NFS plugin for Fuel, read on for general information you should keep in mind throughout the development process.

What to know about Fuel plugins before you begin

Complete information on how to build your plugin is available on the Fuel plugins wiki, which describes best practices for developing code, testing, documentation, and continuous integration (CI). If you want to certify your plugin with Mirantis, you must complete the entire process listed.

Plugin types

Generally you’ll develop plugins to extend the functionality of the four following OpenStack areas:

  1. Compute
  2. Network
  3. Storage
  4. Security

Fuel plugins enable alternative backends for these OpenStack components, :

Note: Keep each Fuel plugin you create in its own repository – we recommend creating a dedicated repo on StackForge to store code, specs, docs, tests, and utils. With everything in one place, other developers can use tools such as Gerrit or Jenkins to reuse the code for CI, which allows plugin code maintenance from release to release.

No matter the type of plugin you develop, the process for creating it is the same. After installation, the Fuel interface displays its elements in the Settings tab.

Note: The current pluggable architecture does not fully support Fuel UI plugins, so the Fuel interface cannot always display complete information. In these cases, use the Fuel CLI to accomplish the necessary tasks.

The structure of a plugin

When creating your plug-in, it must have the following format:

fuel_plugin_name/
├── deployment_scripts
│ └── deploy.sh
├── environment_config.yaml
├── metadata.yaml
├── pre_build_hook
├── repositories
│ ├── centos
│ └── ubuntu
└── tasks.yaml

YAML is a simple language that describes data. The format above specifies the following information:

  • tasks.yaml — specifies when, where, and how to run your scripts
  • metadata.yaml — sets name, version, and compatibility for your plug-in
  • environment_config.yaml — sets plug-in-specific parameters which the user can configure on the Settings tab of the Fuel interface
  • deployment_scripts — a directory where you can add your bash script or puppet manifests
  • repositories — add Ubuntu or CentOS packages required for your plug-in

Supported tasks in Fuel

Fuel supports “shell” and “puppet” tasks, running specified shell commands and Puppet manifests, respectively. An example of a shell task is as follows:

# This task will be applied on controller nodes,
# here you can also specify several roles, for example
# ['cinder', 'compute'] will be applied only on
# cinder and compute nodes
- role: ['controller']
     stage: post_deployment
     type: shell
     parameters:
     cmd: ./deploy.sh
     timeout: 42
# Task is applied for all roles
- role: '*'
     stage: pre_deployment
     type: shell
     parameters:
     cmd: echo all > /tmp/plugin.all
     timeout: 42

In the preceding example, the first task executes the deploy.sh script. ./deploy.sh will be executed in the post_deployment stage, as specified in the task description. The second task creates the /tmp/plugin.all file, with text ‘all’, i.e., it runs the “echo all > /tmp/plugin.all” command.

Executing tasks with Puppet allows you to apply your own Puppet manifests on OpenStack nodes as follows:

  1. Add your site.pp file to the deployment_scripts/puppet/manifests/ directory. puppet_manifest specifies the directory path for the manifest relative to deployment_scripts
  2. Put all required modules in the deployment_scripts/puppet/modules directory – puppet_modules specify the directory path for your modules relative to **deployment_scripts

For example:

# Deployment will be applied on controllers only
- role: ['controller']
     stage: post_deployment
     type: puppet
     parameters:
     puppet_manifest: puppet/manifests/site.pp
     puppet_modules: puppet/modules
     timeout: 360

Fuel does not use Puppet Master. Instead the Fuel task executor copies the manifest from the Fuel Master node and runs the ‘puppet apply’ command on each target node. We recommend using Puppet tasks in your plugin instead of running Puppet in shell tasks.

You can get full details about task formatting on the Fuel wiki. And now we’ll move on to developing your plugin.

Build the NFS plugin

Mirantis has already installed and configured an NFS server, which we use as a backend for Cinder in this NFS plugin example. For more information about this process, refer to the SettingupNFSHowTo.

For now let’s take a look at the first step of plugin creation.

Create a development environment

To build your plugin, you first create a virtual development environment. Using the following commands, create a directory, activate it, and install the Fuel plugin builder:

#mkdir plugins
#cd plugins
#virtualenv .venv
#source .venv/bin/activate
#pip install fuel-plugin-builder

You do not need to be root on the server to build a new plugin in this virtual development environment.

Creating the plugin

Now use the Fuel plugin builder to implement the plugin, running the following command:

#fpb –create external_nfs

For more details on how the Fuel plugin builder works, see the Fuel Plugins wiki.

Plugin backend

To implement the plugin backend, you create a plugin definition in the metadata.yaml as seen below. Here we use Fuel 6.0 with plugins in HA and multinode modes. Full details on implementing the plugin backend are available in the OpenStack Cloud Administrator Guide.

Note: Only plugins created in Mirantis OpenStack 6.0 and higher can be certified.

# Plugin name
name: external_nfs
title: External NFS backend for Cinder
# Plugin version
version: 1.0.0
# Description
description: External NFS backend for Cinder
# Required fuel version
fuel_version: ['6.0']

# The plugin is compatible with releases in the list
releases:
- os: ubuntu
version: 2014.2-6.0
mode: ['ha', 'multinode']
deployment_scripts_path: deployment_scripts/
repository_path: repositories/ubuntu
- os: centos
version: 2014.2-6.0
mode: ['ha', 'multinode']
deployment_scripts_path: deployment_scripts/
repository_path: repositories/centos

# Version of plugin package
package_version: '1.0.0'

Adding a shell task

Now download packages required for NFS Ubuntu and CentOS packages and move them into the external_nfs/repositories/ubuntu and external_nfs/repositories/centos directories to add the appropriate task for package installation.

Save the packages to deployment_scripts/install_packages.sh and add executable permissions for the script “chmod +x install_packages.sh”:

#!/bin/bash

OS_NAME=''
if grep -i CentOS /etc/issue.net >/dev/null; then
OS_NAME='centos';
elif grep -i Ubuntu /etc/issue.net >/dev/null; then
OS_NAME='ubuntu';
fi

function install_package {
if [ $OS_NAME == 'ubuntu' ]; then
apt-get install -y rpcbind nfs-common libevent-2.0 \
libgssglue1 libnfsidmap2 libtirpc1
elif [ $OS_NAME == 'centos' ]; then
yum install -y rpcbind nfs-utils nfs-utils-lib libevent \
key-utils libtirpc libgssglue
fi
}

install_package

Now add a shell task to install the packages. Our tasks.yaml file looks like the following, with the install_packages.sh script running after deployment on the Cinder node:

- role: ['cinder']
stage: post_deployment
type: shell
parameters:
cmd: ./install_packages.sh
timeout: 180

Plugin frontend

Now add a checkbox and plugin-specific elements for the Setting tab of the Fuel interface. We should describe UI in envirionment_config.yaml, addressing the following parameters:

  1. NFS shares file
  2. NFS mount options
  3. Is NFS sparse volumes are used

Add a description of the plugin interface as follows:

attributes:
endpoint:
value: ''
label: 'NFS endpoints'
description: 'comma separated HOST:SHARE values'
weight: 25
type: "text"
mount_options:
value: ''
label: 'NFS mount options'
description: 'optional NFS mount parameters'
weight: 25
type: "text"
nfs_sparsed_volumes:
type: "checkbox"
weight: 30
value: false
label: "NFS sparsed volumes"

description: ""

Passing parameters from the frontend to the backend

Now you must address passed plugin parameters in the deployment process using Puppet manifests and the updated fuel-library.

/etc/astute.yaml passes plugin parameters to the node, which you find in the plugin name section. So we place our plugin parameters in the external_nfs section of the astute.yaml as follows:

external_nfs:
mount_options: ""
endpoint: nfs-server:/nfs nfs_sparsed_volumes: false
metadata:
plugin_version: 1.0.0
weight: 70
toggleable: true
label: External NFS for Cinder
enabled: true

Adding a Puppet task

Puppet manifests perform tasks as described in the OpenStack Cloud Administrator Guide and put them into the deployment_scripts/puppet directory in GitHub. After applying Puppet manifests, pay attention to the rights on the /etc/nfsshares files and run the following to restart the Cinder service:

external_nfs/deployment_scripts/puppet/modules/cindernfs/manifests/backend/nfs.pp.

Now add task to apply manifests into tasks.yaml:

- role: ['cinder']
stage: post_deployment
type: puppet
parameters:
puppet_manifest: puppet/site.pp
puppet_modules: puppet/modules/
timeout: 360

Building the plugin

Add a pre_build_hook to build the plugin with the appropriate version of the fuel-library:

#!/bin/bash

set -eux

ROOT="$(dirname `readlink -f $0`)"
MODULES="${ROOT}"/deployment_scripts/puppet/modules
TMP_DIR="${ROOT}"/tmp
mkdir -p "${MODULES}"
mkdir -p "${TMP_DIR}"
REPO_PATH='https://github.com/stackforge/fuel-library/tarball/f43d885914d74fbd062096763222f350f47480e1'

wget -qO- "${REPO_PATH}" | \
tar -C "${MODULES}" --strip-components=3 -zxvf - \
stackforge-fuel-library-f43d885/deployment/puppet/{inifile,stdlib}

You can now build your package as follows:

fpb --build external_nfs

The package will be saved as external_nfs/external_nfs-1.0.0.fp, and you can now deploy your plugin using VirtualBox virtual machines as shown in the Mirantis Quick Start Guide.

Note: Take snapshots of your virtual machines after deploying OpenStack so you can revert to a clean installation if something goes wrong.

Deploy the plugin

Your plugin created, copy it to the Fuel Master node and install it as follows to deploy:

scp external_nfs/external_nfs-1.0.0.fp root@master-node:
ssh root@master-node
fuel plugins --install external_nfs-1.0.0.fp

After installation, you can add the environment to the Fuel interface and configure the NFS plugin on the Settings tab, add nodes, and test the plugin. Before we get to testing, let’s look at some issues that may require troubleshooting.

Troubleshooting

One issue that frequently occurs in building a plugin is a file that’s present, but not findable. For example, let’s assume that you had not specified the relative path for the install_packages.sh script:

- role: ['cinder']
stage: post_deployment
type: shell
parameters:
cmd: install_packages.sh
timeout: 180

In this case, the deployment would fail, with the following error:

Deployment has failed. Method deploy. Failed to deploy plugin external_nfs-1.0.0. Inspect Astute logs for the details.

As suggested, you’ll want to grep log the /var/log/docker-logs/astute.log, which in this case would show:

2014-12-23T16:31:54 debug: [395] c5d1e2b5-56bb-4428-9666-8c5574cb06a4: MC agent 'execute_shell_command', method 'execute', results: {:sender=>"3", :statuscode=>0, :statusmsg=>"OK", :data=>{:stdout=>"", :exit_code=>127, :stderr=>"sh: 1: install_packages.sh: not found\n"}}
2014-12-23T16:31:54 debug: [395] c5d1e2b5-56bb-4428-9666-8c5574cb06a4: cmd: cd /etc/fuel/plugins/external_nfs-1.0.0/ && install_packages.sh
cwd: /etc/fuel/plugins/external_nfs-1.0.0/
stdout:
stderr: sh: 1: install_packages.sh: not found

Grepping the Astute log as seen above reveals the path to the install_packages.sh in the tasks.yaml is incorrect. Use a relative path from the deployment_scripts, as follows:

- role: ['cinder']
stage: post_deployment
type: shell
parameters:
cmd: ./install_packages.sh
timeout: 180

After correcting the problem, you can build a package and update it on the Fuel Master node:

fpb --build external_nfs
scp external_nfs/external_nfs-1.0.0.fp root@master-node:
ssh root@master-node
fuel plugins --force external_nfs-1.0.0.fp

Check results

Having solved the problem, you can create a new environment, configure, and deploy it. After successful deployment, check that Cinder works properly with the NFS backend as follows:

1. Login to the Controller node, and create a Cinder volume:

ssh root@master-node
ssh controller-node
source openrc
cinder create 1

2. Check the volume status via the Fuel CLI. See the Mirantis OpenStack User Guide for more information.

cinder list

+--------------------------------------+-----------+--------------+------+-------------+----------+-------------+
| ID | Status | Display Name | Size | Volume Type | Bootable | Attached to |
+--------------------------------------+-----------+--------------+------+-------------+----------+-------------+
| d9382a2b-fd13-424b-9eba-3a77a1d16008 | available | None | 1 | None | false | |
+--------------------------------------+-----------+--------------+------+-------------+----------+-------------+

3. Check that the volume with the ID from the cinder list output is created on the NFS share as a file with a name equal to the volume-Id value:

nfs-server$ ls
volume-d9382a2b-fd13-424b-9eba-3a77a1d16008

If you want to fix the Puppet manifests on the Cinder node, you can find them in the directory name /etc/fuel/plugins/external_nfs-1.0.0/puppet and apply the following:

ssh root@master-node
ssh cinder-node
cd /etc/fuel/plugins/external_nfs-1.0.0/
puppet apply --modulepath=/etc/fuel/plugins/external_nfs-1.0.0/puppet/modules site.pp

For more information about debugging your plugin, see the Fuel Plugin wiki.

Release the plugin

After creating a plugin you can exercise any of the following options:

  1. Deploy and use the plugin independently without informing Mirantis
  2. Submit the plugin to Mirantis without requesting certification, allowing people who download Mirantis OpenStack to download the plugin also, but without Mirantis support for the installation.
  3. Submit the plugin and apply for certification, with qualifying plugins eligible for Mirantis support.

The certification process takes some time, with approved plugins added to the official Fuel Plugin Catalog and eligible for support at the successful conclusion of the required steps outlined below.

Mirantis certification for plugins

If you decide you want to certify your plugin, you must provide the following to Mirantis:

  • Design specification
  • Installation/deployment guide
  • User guide
  • Test plan
  • Test report

Details about how to structure your guide are available here.

The Mirantis plugin certification process itself includes the following steps::

  • Design spec review
  • Code review
  • Test plan review
  • Deployment guide review
  • User guide review
  • Test report analysis
  • Plugin testing procedures

Note: Granting access to plugins is not required to enable them with Fuel or to certify the plugin with Mirantis.

Greater OpenStack functionality with plugins

By creating Fuel plugins, you extend OpenStack functionality. While our example was an NFS plugin, you can create other types to solve issues you encounter in your environment with the flexibility Fuel provides for deployment and management. In addition, you get Mirantis support for the plugin when officially certified using the processes we’ve outlined here. Whichever you decide, plugins are a good way to create an optimized OpenStack environment. Currently your plugins can be used in the pre-deployment and post-deployment phase only.

Subscribe to Our Newsletter

Latest Tweets

Suggested Content

LIVE DEMO
Mirantis Cloud Platform
WEBINAR
Top Sysadmin Tasks and How to Do Them with OpenStack
WEBINAR
ONAP Overview