OpenStack Orchestration (Heat)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

29 KiB

Heat Resource Plug-in Development Guide

Heat allows service providers to extend the capabilities of the orchestration service by writing their own resource plug-ins. These plug-ins are written in Python and included in a directory configured by the service provider. This guide describes a resource plug-in structure and life cycle in order to assist developers in writing their own resource plug-ins.

Resource Plug-in Life Cycle

A resource plug-in is relatively simple in that it needs to extend a base Resource class and implement some relevant life cycle handler methods. The basic life cycle methods of a resource are:

create

The plug-in should create a new physical resource.

update

The plug-in should update an existing resource with new configuration or tell the engine that the resource must be destroyed and re-created. This method is optional; the default behavior is to create a replacement resource and then delete the old resource.

suspend

The plug-in should suspend operation of the physical resource; this is an optional operation.

resume

The plug-in should resume operation of the physical resource; this is an optional operation.

delete

The plug-in should delete the physical resource.

The base class Resource implements each of these life cycle methods and defines one or more handler methods that plug-ins can implement in order to manifest and manage the actual physical resource abstracted by the plug-in. These handler methods will be described in detail in the following sections.

Heat Resource Base Class

Plug-ins must extend the class heat.engine.resource.Resource.

This class is responsible for managing the overall life cycle of the plug-in. It defines methods corresponding to the life cycle as well as the basic hooks for plug-ins to handle the work of communicating with specific down-stream services. For example, when the engine determines it is time to create a resource, it calls the create method of the applicable plug-in. This method is implemented in the Resource base class and handles most of the bookkeeping and interaction with the engine. This method then calls a handle_create method defined in the plug-in class (if implemented) which is responsible for using specific service calls or other methods needed to instantiate the desired physical resource (server, network, volume, etc).

Resource Status and Action

The base class handles reporting state of the resource back to the engine. A resource's state is the combination of the life cycle action and the status of that action. For example, if a resource is created successfully, the state of that resource will be CREATE_COMPLETE. Alternatively, if the plug-in encounters an error when attempting to create the physical resource, the state would be CREATE_FAILED. The base class handles the reporting and persisting of resource state, so a plug-in's handler methods only need to return data or raise exceptions as appropriate.

Resource Support Status

New resource should be marked from which OpenStack release it will be available with support_status option. For more details, see supportstatus.

Resource description

An important part of future resources is a concisely written description. It should be in class docstring and contain information about the resource and how it could be useful to the end-user. The docstring description is used in documentation generation and should be always defined, if resource is designed for public use. Docstring should follows PEP 257.

Properties and Attributes

A resource's properties define the settings the template author can manipulate when including that resource in a template. Some examples would be:

  • Which flavor and image to use for a Nova server
  • The port to listen to on Neutron LBaaS nodes
  • The size of a Cinder volume

Note

Properties should normally be accessed through self.properties. This resolves intrinsic functions, provides default values when required and performs property translation for backward compatible schema changes. The self.properties.data dict provides access to the raw data supplied by the user in the template without any of those transformations.

Attributes describe runtime state data of the physical resource that the plug-in can expose to other resources in a Stack. Generally, these aren't available until the physical resource has been created and is in a usable state. Some examples would be:

  • The host id of a Nova server
  • The status of a Neutron network
  • The creation time of a Cinder volume

Defining Resource Properties

Each property that a resource supports must be defined in a schema that informs the engine and validation logic what the properties are, what type each is, and validation constraints. The schema is a dictionary whose keys define property names and whose values describe the constraints on that property. This dictionary must be assigned to the properties_schema attribute of the plug-in.

As shown above, some properties may themselves be complex and reference nested schema definitions. Following are the parameters to the Schema constructor; all but the first have defaults.

data_type:

Defines the type of the property's value. The valid types are the members of the list properties.Schema.TYPES, currently INTEGER, STRING, NUMBER, BOOLEAN, MAP, LIST and ANY; please use those symbolic names rather than the literals to which they are equated. For LIST and MAP type properties, the schema referenced constrains the format of complex items in the list or map.

description:

A description of the property and its function; also used in documentation generation. Default is None --- but you should always provide a description.

default:

The default value to assign to this property if none was supplied in the template. Default is None.

schema:

This property's value is complex and its members must conform to this referenced schema in order to be valid. The referenced schema dictionary has the same format as the properties_schema. Default is None.

required:

True if the property must have a value for the template to be valid; False otherwise. The default is False

constraints:

A list of constraints that apply to the property's value. See Property Constraints.

update_allowed:

True if an existing resource can be updated, False means update is accomplished by delete and re-create. Default is False.

immutable:

True means updates are not supported, resource update will fail on every change of this property. False otherwise. Default is False.

support_status:

Defines current status of the property. Read supportstatus for details.

Accessing property values of the plug-in at runtime is then a simple call to:

Based on the property type, properties without a set value will return the default "empty" value for that type:

Type Empty Value
String

''

Number

0

Integer

0

List

[]

Map

{}

Boolean

False

Property Constraints

Following are the available kinds of constraints. The description is optional and, if given, states the constraint in plain language for the end user.

AllowedPattern(regex, description):

Constrains the value to match the given regular expression; applicable to STRING.

AllowedValues(allowed, description):

Lists the allowed values. allowed must be a collections.Sequence or basestring. Applicable to all types of value except MAP.

Length(min, max, description):

Constrains the length of the value. Applicable to STRING, LIST, MAP. Both min and max default to None.

Range(min, max, description):

Constrains a numerical value. Applicable to INTEGER and NUMBER. Both min and max default to None.

Modulo(step, offset, description):

Starting with the specified offset, every multiple of step is a valid value. Applicable to INTEGER and NUMBER.

Available from template version 2017-02-24.

CustomConstraint(name, description, environment):

This constructor brings in a named constraint class from an environment. If the given environment is None (its default) then the environment used is the global one.

Defining Resource Attributes

Attributes communicate runtime state of the physical resource. Note that some plug-ins do not define any attributes and doing so is optional. If the plug-in needs to expose attributes, it will define an attributes_schema similar to the properties schema described above. Each item in the schema dictionary consists of an attribute name and an attribute Schema object.

Following are the parameters to the Schema.

description

A description of the attribute; also used in documentation generation. Default is None --- but you should always provide a description.

type

Defines the type of attribute value. The valid types are the members of the list attributes.Schema.TYPES, currently STRING, NUMBER, BOOLEAN, MAP, and LIST; please use those symbolic names rather than the literals to which they are equated.

support_status

Defines current status of the attribute. Read supportstatus for details.

If attributes are defined, their values must also be resolved by the plug-in. The simplest way to do this is to override the _resolve_attribute method from the Resource class:

If the plug-in needs to be more sophisticated in its attribute resolution, the plug-in may instead choose to override FnGetAtt. However, if this method is chosen, validation and accessibility of the attribute would be the plug-in's responsibility.

Also, each resource has show attribute by default. The attribute uses default implementation from heat.engine.resource.Resource class, but if resource has different way of resolving show attribute, the _show_resource method from the Resource class will need to be overridden:

Property and Attribute Example

Assume the following simple property and attribute definition:

Also assume the plug-in defining the above has been registered under the template reference name 'Resource::Foo' (see Registering Resource Plug-ins). A template author could then use this plug-in in a stack by simply making following declarations in a template:

Life Cycle Handler Methods

To do the work of managing the physical resource the plug-in supports, the following life cycle handler methods should be implemented. Note that the plug-in need not implement all of these methods; optional handlers will be documented as such.

Generally, the handler methods follow a basic pattern. The basic handler method for any life cycle step follows the format handle_<life cycle step>. So for the create step, the handler method would be handle_create. Once a handler is called, an optional check_<life cycle step>_complete may also be implemented so that the plug-in may return immediately from the basic handler and then take advantage of cooperative multi-threading built in to the base class and periodically poll a down-stream service for completion; the check method is polled until it returns True. Again, for the create step, this method would be check_create_complete.

Create

Update (Optional)

Note that there is a default implementation of handle_update in heat.engine.resource.Resource that simply raises an exception indicating that updates require the engine to delete and re-create the resource (this is the default behavior) so implementing this is optional.

Suspend (Optional)

These handler functions are optional and only need to be implemented if the physical resource supports suspending

Resume (Optional)

These handler functions are optional and only need to be implemented if the physical resource supports resuming from a suspended state

Delete

Resource Dependencies

Ideally, your resource should not have any 'hidden' dependencies, i.e. Heat should be able to infer any inbound or outbound dependencies of your resource instances from resource properties and the other resources/resource attributes they reference. This is handled by heat.engine.resource.Resource.add_dependencies().

If this is not possible, please do not simply override add_dependencies() in your resource plugin! This has previously caused problems for multiple operations, usually due to uncaught exceptions, If you feel you need to override add_dependencies(), please reach out to Heat developers on the #heat IRC channel on FreeNode or on the openstack-discuss mailing list to discuss the possibility of a better solution.

Registering Resource Plug-ins

To make your plug-in available for use in stack templates, the plug-in must register a reference name with the engine. This is done by defining a resource_mapping function in your plug-in module that returns a map of template resource type names and their corresponding implementation classes:

def resource_mapping():
    return { 'My::Custom::Plugin': MyResourceClass }

This would allow a template author to define a resource as:

Note that you can define multiple plug-ins per module by simply returning a map containing a unique template type name for each. You may also use this to register a single resource plug-in under multiple template type names (which you would only want to do when constrained by backwards compatibility).

Configuring the Engine

In order to use your plug-in, Heat must be configured to read your resources from a particular directory. The plugin_dirs configuration option lists the directories on the local file system where the engine will search for plug-ins. Simply place the file containing your resource in one of these directories and the engine will make them available next time the service starts.

See ../configuration/index for more information on configuring the orchestration service.

Testing

Tests can live inside the plug-in under the tests namespace/directory. The Heat plug-in loader will implicitly not load anything under that directory. This is useful when your plug-in tests have dependencies you don't want installed in production.

Putting It All Together

You can find the plugin classes in heat/engine/resources. An exceptionally simple one to start with is random_string.py; it is unusual in that it does not manipulate anything in the cloud!

Resource Contributions

The Heat team is interested in adding new resources that give Heat access to additional OpenStack or StackForge projects. The following checklist defines the requirements for a candidate resource to be considered for inclusion:

  • Must wrap an OpenStack or StackForge project, or a third party project that is relevant to OpenStack users.
  • Must have its dependencies listed in OpenStack's global-requirements.txt file, or else it should be able to conditionally disable itself when there are missing dependencies, without crashing or otherwise affecting the normal operation of the heat-engine service.
  • The resource's support status flag must be set to UNSUPPORTED, to indicate that the Heat team is not responsible for supporting this resource.
  • The code must be of comparable quality to official resources. The Heat team can help with this during the review phase.

If you have a resource that is a good fit, you are welcome to contact the Heat team. If for any reason your resource does not meet the above requirements, but you still think it can be useful to other users, you are encouraged to host it on your own repository and share it as a regular Python installable package. You can find example resource plug-ins that have all the required packaging files in the contrib directory of the official Heat git repository.