Create resource for the puppet handler

Let’s create an example Resource for the puppet Handler version 1 [1]. The resource should install and configure OpenStack Nova API service.

[1]There is also puppet handler version 2 but it is out of the scope of this example.

Step 1: Find an appropriate puppet module

The Puppet OpenStack module for Nova provides all of the required functionality.

Step 2: Define granularity level for a resource

One may want to implement resources as atomic entities doing their only single task, like running one and only puppet manifest [2]. Other option might be single entity doing all required tasks instead. In order to configure and run the Nova API service at least two manifests should be executed: init.pp and api.pp [3].

[2]Puppet manifests may contain references to externally defined classess or services in the catalog. Keep that in mind then designing the resource.
[3]This assumes configuring DB and messaging entities like user, password database, vhost, access rights are left out of the scope of this example.

Assuming the atomic tasks approach, the example resource for Nova API service should only use the api.pp manifest. Note that the puppet handler is normally executed in its own isolated puppet catalog with its specific hiera data only. This assumes every puppet manifest called by every action to be executed as a separate puppet run and shares nothing with other tasks.

Step 3: Define resource inputs

Once the granularity level of the resource is clearly defined, one should define the resource’s Input data. The puppet class nova::api contains lots of parameters. It looks reasonable to use them as the resource inputs as is.


There is a helper script to convert a puppet class parameters into the format expected by the meta.yaml inputs file.

Step 4: Implement basic action run

Each resource should have all of the mandatory actions defined. In this example we define only the ref-action-term run. With the example of Nova API resource, the action run should:

  • fetch the resource inputs from the hiera [4]

    $resource = hiera($::resource_name)
    $ensure_package = $resource['input']['ensure_package']
    $auth_strategy = $resource['input']['auth_strategy']
[4]The syntax is the puppet handler v1 specific. The v2 allows to query the hiera directly, like $public_vip = hiera(‘public_vip’)
  • call the class { ‘nova::api’: } with the required parameters

  • implement workarounds for externally referenced entities, like

    exec { 'post-nova_config':
      command     => '/bin/echo "Nova config has changed"',
    include nova::params
    package { 'nova-common':
      name   => $nova::params::common_package_name,
      ensure => $ensure_package,


Otherwise, called class would assume the package and exec are already included in the catalog by the init.pp. And would fail as there is no class { ‘nova’: } call expected for the Nova API resource action run. In order to implement the resource without such workarounds, one should rethink the granularity scope for the resource. And make sure the resource contains required inputs for the main nova and nova::api classes and call them both in the resource action run.

Step 5: Think of the rest of the resource actions

One should also design other actions for the resource. Mandatory are only run, update and remove. There might be additional ones like on-fail, on-retry or whichever are actually required to implement expected behavior. For the given API resource there are no specific actions expected and there is nothing to do for the action remove. For the action update, it is likely the same steps should be done as for the action run.

Step 6: Design the high level functional test

TODO(bogdando) provide details about test.py and writing tests for Nova API in order to verify if it works on the app level.

Step 7: Think of the deployment composition

The deployment composition is which resources should be used and in which order it should be executed to achieve the expected result, which is a successful Deployment plan. For the given example, the composition may be as following:

  • Install and configure MySQL DB [5]
  • Install and configure RabbitMQ node
  • Install and configure dependency components like OpenStack Keystone
  • Create all of the required user/tenant/db/vhost entities and assign rights
  • Install and configure Nova main components, like packages, db sync, configs.
  • Install and configure Nova API. BINGO! A job for our resource, at last!
[5]Omitted host related steps like OS provisioning, disks and network configuration.

Besides the execution plan, there is also data Connection to be considered. For example, one might want to have all of the OpenStack services to use the common RabbitMQ virtualhost and user. Or have them separated instead. Or use the clustered RabbitMQ nodes. These decisions will directly impact how resources’ inputs should be connected.