Nailgun Extensions

Nailgun Extensions

Overview of extensions

Nailgun extensions provide a capability for Fuel Developers to extend Fuel features. Extensions were introduced to provide pythonic way for adding integrations with external services, extending existing features, or adding new features without changing the Nailgun source code.

A Nailgun extension can execute its method on specific events such as on_node_create or on_cluster_delete (more about event handlers in the Available Events section) and also to change deployment and provisioning data just before it is sent to orchestrator by the means of Data Pipelines classes.


The extensions mechanism does not provide a sufficient level of isolation. Therefore, the extension may not work after you upgrade Fuel.

On the contrary, Fuel plugins provide backward compatibility and a friendly UI for the end user. Use plugins for all changes in the system performed by the Fuel user.

Required properties

All Nailgun extensions must populate the following class variables:

  • name - a string which will be used inside Nailgun to identify the extension. It should consist only of lowercase letters with “_” (underscore) separator and digits.
  • version - a string with version. It should follow semantic versioning:
  • description - a short text which briefly describes the actions that the extension performs.

Available events

Extension can execute event handlers on specific events. There is a list of available handlers:

Method Event
on_node_create Node has been created
on_node_update Node has been updated
on_node_reset Node has been reseted
on_node_delete Node has been deleted
on_node_collection_delete Collection of nodes has been deleted
on_cluster_delete Cluster has been deleted
on_before_deployment_check Called right before running “before deployment check task”

REST API handlers

Nailgun Extensions also provide a way to add additional API endpoints. To add an extension-specific handler sub-class from:


The second step is to register the handler by providing the urls list in Extension class:

urls = [
    {'uri': r'/example_extension/(?P<node_id>\d+)/?$',
     'handler': ExampleNodeHandler},
    {'uri': r'/example_extension/(?P<node_id>\d+)/properties/',
     'handler': NodeDefaultsDisksHandler},

As you can see you need to provide a list of dicts with keys:

key value
uri a regular expression (string) for the URL path
handler handler class

Database interaction

There is a possibility to use the Nailgun database to store the data needed by a Nailgun extension. To use it you must provide alembic migration scripts which should be placed in:


Where extension_module is the one where the file with your extension class is placed.

You can also change this directory by overriding the classmethod:


It should return an absolute path (string) to alembic migrations directory.

Additionally, use a table name with an extension-specific prefix in models classes and alembic migration scripts. We recommend that you use the table_prefix extension method to retrieve the prefix (string).


Do not use the direct db calls to Nailgun core tables in the extension class. Use the nailgun.objects module which ensures compatibility between the Nailgun DB and the configuration implemented in your extension.

There must be no relations between extension models and core models.

Extension Data Pipelines

If you want to change the deployment or provisioning data just before it is sent to an orchestrator use Extension Data Pipelines.

Data Pipeline is a class which inherits from:


BasePipeline provides two methods which you can override:

  • process_provisioning
  • process_deployment

Both methods take the following parameters:

  • data - serialized data which will be sent to orchestrator. Data does not include nodes data which was defined by User in replaced_deployment_info or in replaced_provisioning_info.
  • cluster - a cluster instance for which the data was serialized.
  • nodes - nodes instances for which the data was serialized. Nodes list does not include node instances which were filtered out in data parameter.
  • **kwargs - additional kwargs - must be in method definition to provide backwards-compatibility for future (small) changes in extensions API.

Both methods must return the data dict so it can be processed by other pipelines.

To enable pipelines, add the data_pipelines variable in your extensions class:

class ExamplePipelineOne(BasePipeline):

    def process_provisioning(cls, data, cluster, nodes, **kwargs):
        data['new_field'] = 'example_value'
        return data

    def process_deployment(cls, data, cluster, nodes, **kwargs):
        data['new_field'] = 'example_value'
        return data

class ExamplePipelineTwo(BasePipeline):

    def process_deployment(cls, data, cluster, nodes, **kwargs):
        data['new_field2'] = 'example_value2'
        return data

class ExampleExtension(BaseExtension):
    data_pipelines = [

Pipeline classes will be executed in the order they are defined in the data_pipelines variable.

How to install and plug in extensions

To use extensions system in Nailgun, implement an extension class which will be the subclass of:


The class must be placed in a separate module which defines entry_points in its file.

Extension entry point should use Nailgun extensions namespace which is:


Example file with ExampleExtension may look like this:

from setuptools import setup, find_packages

       description='Demonstration package for Nailgun Extensions',
       author='Fuel Nailgman',
       classifiers=['Development Status :: 3 - Alpha',
                   'License :: OSI Approved :: Apache Software License',
                   'Programming Language :: Python',
                   'Programming Language :: Python :: 2',
                   'Environment :: Console',
          'nailgun.extensions': [
              'ExampleExtension = example_package.nailgun_extensions.ExampleExtension',

Now to enable the extension it is enough to run:

python install


pip install .

Now extension will be discovered by Nailgun automatically after restart.

Example Extension with Pipeline - additional logging

import datetime
import logging

from nailgun.extensions import BaseExtension
from nailgun.extensions import BasePipeline

logger = logging.getLogger(__name__)

class TimeStartedPipeline(BasePipeline):

    def process_provisioning(cls, data, cluster, nodes, **kwargs):
        now =
        data['time_started'] = 'provisioning started at {}'.format(now)
        return data

    def process_deployment(cls, data, cluster, nodes, **kwargs):
        now =
        data['time_started'] = 'deployment started at {}'.format(now)
        return data

class ExampleExtension(BaseExtension):
    name = 'additional_logger'
    version = '1.0.0'
    description = 'Additional Logging Extension '

    data_pipelines = [

    def on_node_create(cls, node):
        logging.debug('Node %s has been created',

    def on_node_update(cls, node):
        logging.debug('Node %s has been updated',

    def on_node_reset(cls, node):
        logging.debug('Node %s has been reseted',

    def on_node_delete(cls, node):
        logging.debug('Node %s has been deleted',

    def on_node_collection_delete(cls, node_ids):
        logging.debug('Nodes %s have been deleted', ', '.join(node_ids))

    def on_cluster_delete(cls, cluster):
        logging.debug('Cluster %s has been deleted',

    def on_before_deployment_check(cls, cluster):
        logging.debug('Cluster %s will be deployed soon',
Creative Commons Attribution 3.0 License

Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.