charms.openstack provides a module charms_openstack which is included in
layer-openstack’s wheelhouse.txt. It is provides the fundamental
functionality required of most OpenStack charms.
The main classes that the module provides are:
:class:OpenStackRelationAdapter
:class:RabbitMQRelationAdapter
:class:DatabaseRelationAdapter
:class:ConfigurationAdapter
:class:OpenStackRelationsAdapter
:class:OpenStackCharm
Key features of charms.openstack
The main features that charms.openstack provides are:
a base OpenStackCharm that provides:
The ability to specify the OpenStack release that the charm works with.
The list of packages to install on the charm.
The ports that the charm exposes.
The keystone service type (if applicable)
A mapping of config files to services to restart if the configuration
changes.
The required relations for the charm (workload status)
The sync command that the database (if associated) will need for its
schema.
a default install that gets the packages, installs them, and sets the
appropriate workload status.
A configuration file renderer (using the relation adapters) to write
the configuration files for the service being managed.
A workload status helper (assess_status()) that checks the state of
interfaces, the services, and ports, and sets the workload status. This
is automatically provided for the update-status hook in the layer-openstack
layer.
How to leverage charms.openstack classes
Using OpenStackCharm
OpenStackCharm() and the related classes provide a powerful framework to
build an OpenStack charm on. There are two approaches to writing charms that
support multiple OpenStack releases. Note that determining the release is up
to the charm author, and can be signalled to OpenStackCharm in two ways.
Write a single OpenStackCharm derived class that uses self.release to
determine what functionality to exhibit depending on the release. In this
case, there is no need to register multiple charms and provide a chooser
to determine which class to use.
Write multiple OpenStackCharm derived classes which map to each difference
in charm functionality depending on the release, and register a chooser
function using the @register_os_release_selector decorator.
e.g.
class LibertyCharm(OpenStackCharm):
release = 'liberty'
class MitakaCharm(OpenStackCharm):
release = 'mitaka'
@register_os_release_selector
def choose_release():
"""Determine the release based on the python-keystonemiddleware that is
installed.
"""
return ch_utils.os_release('python-keystonemiddleware')
This will automatically select LibertyCharm for a liberty release and
MitakaCharm for the mitaka release. Note, that it will also setrelease
on the OpenStackCharm instance via the __init__() method, so that the
instance knows what the charm is.
If only a single charm class is needed, the __init__() method of the
class can be used to determine the release instead:
class TheCharm(OpenStackCharm):
release = 'liberty'
def __init__(release=None, *args, **kwargs):
if release is None:
release = ch_utils.os_release('python-keystonemiddleware')
super(TheCharm, self).__init__(release=release, *args, **kwargs)
If the release selector function is registered, then the overridden
__init__() method is not needed as the release will be passed into the
default __init__() method. However, there may be other functionality that
the charm author needs to include in the initialiser.
Note that using os_release() can typically be used to determine the release
of OpenStack.
Using the relation adapter classes - OpenStackRelationAdapter
The relation adapter classes adapt a reactive interface for use in the
rendering functions. Their principal use is to provide an iterator of the
attributes declared in the assessors attribute of the instance.
A reactive BaseRelation derived instance has an auto_accessors attribute
which declares the variables that the relation has. These are copied into the
accessors attribute of the OpenStackRelationAdapter class, and additional
attributes can be added as part of class instantiation.
Note that the accessor properties are dynamic, in that they call the
underlying relation property when they are accessed.
The purpose of the OpenStackRelation class is for the instance to be used
as part of configuration file rendering, as an instance of an
OpenStackRelation class can be passed to the render function, and the
iterator will provide the key value pairs to the template processor.
A derived OpenStackRelation class can provide additional computed
properties as required. e.g. the RabbitMQRelationAdapter implementation:
class RabbitMQRelationAdapter(OpenStackRelationAdapter):
"""
Adapter for the RabbitMQRequires relation interface.
"""
interface_type = "messaging"
def __init__(self, relation):
add_accessors = ['vhost', 'username']
super(RabbitMQRelationAdapter, self).__init__(relation, add_accessors)
@property
def host(self):
"""
Hostname that should be used to access RabbitMQ.
"""
if self.vip:
return self.vip
else:
return self.private_address
@property
def hosts(self):
"""
Comma separated list of hosts that should be used
to access RabbitMQ.
"""
hosts = self.relation.rabbitmq_hosts()
if len(hosts) > 1:
return ','.join(hosts)
else:
return None
Note that the additional accessors vhost and username are provided in the
overridden __init__() method.
The ConfigurationAdapter
The ConfigurationAdapter class simply provides snapshot of the
configuration options for the current charm, such that they can be accessed
as attributes of an instance of the class. e.g. rather than config('vip')
then user can use c_adapter.vip.
The benefit, is that a derived version of ConfigurationAdapter can be
provided that has computed properties that can be used like static properties
on the instance. The ConfigurationAdapter, or derived class, is used with
the OpenStackRelationAdapters class (not the plural …Adapters) class that
brings together all of the relations into one place.
The OpenStackRelationAdapters class
The OpenStackRelationAdapters class joins together the relation adapter
classes, with the ConfigurationAdapter (or derived) class, and works like
a charmhelpers OSRenderConfig instance to the rendering functions in
charmhelpers.
Thus an instance of the OpenStackRelationAdapters (or derived) class is used
in the charmhelpers.core.templating.render() function to provide the
variables needed to render templates.
The OpenStackRelationAdapters class can be subclassed (derived) with
additional custom OpenStackRelationAdapter classes (to map to particular
relations) using the relation_adapters class property:
class MyRelationAdapters(OpenStackRelationAdapters):
relation_adapters = {
'my-relation': MyRelationAdapter,
}
This enables custom relation adapters to be mapped to particular relations
such that custom functionality can be implemented for a particular reactive
relationship.
HighAvailability Support
To be completed.
OpenStack Upgrade via config
An OpenStack principle charm has an ‘openstack-origin’ configuration option.
This is used to setup the package source for a charm. If a user updates this
option to point at a package repository then the charm can be configured to
automatically upgrade. This is achieved with the following steps:
OpenStack charms support the concept of workload status which helps to inform
a user of the charm of the current state of the charm. The following workload
statuses are supported:
unknown - The charm doesn’t support workload status. This should not
be used for charms that DO support workload status.
active - The unit under the charms control is fully configuration
and available for use.
maintenance - the unit is installing, or doing something of that nature.
waiting - The unit is waiting for a relation to become available. i.e. the
relation is not yet complete in that some data is missing still.
blocked - a relation is not yet connected, or some other blocking
condition.
paused - (Not yet available) - the unit has been put into the paused state.
The default is for charms to support workload status, and the default
installation method sets the status to maintenance with an install message.
If the charm is not going to support workload status, and this is not
recommended, then the charm author will need to override the install()
method of OpenStackCharm derived class to disable setting the maintenance
state, and override the assess_status() method to a NOP.
The assess_status() method on OpenStackCharm provides a helper to enable
the charm author to provide workload status. By default:
The actual assessment of status is deferred until the all of the reactive
handlers have had a chance to execute (according to their conditions), just
before the charm hook exits. The real assess_status() method is actually
_assess_status() and the assess_status() method simply sets up an
atexit() hook to defer the operation. This means that you can call
assess_status() multiple times BUT it will actually only be invoked at the
end of the charm hook execution. If you need to actually run
assess_status() at the point in the handler, then call _assess_status().
The install method provides the maintenance status.
The layer-openstack layer provides a hook for update-status which
calls the assess_status() function on the charm class.
The _assess_status() method uses various attributes of the class to provide
a default mechanism for assessing the workload status of the charm/unit.
The latter is extremely useful for determining the workload status. The
_assess_status() method does the following checks:
The unit checks if it is paused. (Not yet available as a feature).
The unit checks the relations to see if they are connected and available.
The unit checks custom_assess_status_check()
The unit checks that the services are running and ports are open.
Checking of relations
The assess_status function checks that the relations named in the class
attribute required_relations are connected and available. It does this using
the convention of:
A connected relation has the {relation}.connected state set.
An available relation has the {relation}.available state set.
This is a convention that the interfaces (e.g. interface-keystone, etc.) use.
interface-keystone sets identity-service.connected when it has a connection
with keystone, and identity-service.available when the connection is
completed and all information transferred.
That if required_relations is ['identity-service'], then the
assess_status() function will check for identity-service.connected and
identity-service.available states.
If the charm author requires additional states to be checked for an interface,
then the method states_to_check should be overridden in the derived class and
additional states, the status and error message provided. See the code for
further details.
e.g.
def states_to_check():
states = super(MyCharm, self).states_to_check()
states['some-relation'].append(
("some-relation.available.ssl", "waiting", "'some-relation' incomplete"))
return states
The custom_assess_status_check() method
If the charm author needs to do additional status checking, then the
custom_assess_status_check() method should be overridden in the derived
class. The return value from the method is:
(None, None) - the unit is fine.
status, message - the unit’s workload status is not active.
Not checking services are running
By default, the _assess_status() method checks that the services declared in
the class attribute services (list of strings) are checked to ensure that
they are running. Additionally, the ports declared in the class attribute
api_ports are also checked for being listened on.
However, if the services check is not required, then the derived class should
overload the check_running_services() method and return None, None.
Additionally, if the services running check is required, but the ports should
not be checked, then the ports_to_check method can be overridden and return
an empty list [].
Using assess_status()
The assess_status() method should be used on any hook or state method where
the unit’s status may have changed. e.g. interfaces connecting or becoming
available, configuration changes, etc.
e.g.
@reactive.when('amqp.connected')
def setup_amqp_req(amqp):¬
"""Use the amqp interface to request access to the amqp broker using our
local configuration.
"""
amqp.request_access(username=hookenv.config('rabbit-user'),
vhost=hookenv.config('rabbit-vhost'))
MyCharm.singleton.assess_status()
Team and repository tags
charms.openstack
Helpers for building layered, reactive OpenStack charms.
Support and discussions
We use the openstack-charmers mailing-lists for developer and user discussions, you can find and subscribe here: https://lists.ubuntu.com/openstack-charmers.
If you prefer live discussions, some of us also hang out in #juju on Charmhub chat.
Bug reports
Bug reports can be filed at https://bugs.launchpad.net/charms.openstack/+filebug
Using
charms.openstackcharms.openstack provides a module
charms_openstackwhich is included in layer-openstack’swheelhouse.txt. It is provides the fundamental functionality required of most OpenStack charms.The main classes that the module provides are:
OpenStackRelationAdapterRabbitMQRelationAdapterDatabaseRelationAdapterConfigurationAdapterOpenStackRelationsAdapterOpenStackCharmKey features of
charms.openstackThe main features that
charms.openstackprovides are:OpenStackCharmthat provides:assess_status()) that checks the state of interfaces, the services, and ports, and sets the workload status. This is automatically provided for theupdate-statushook in thelayer-openstacklayer.How to leverage
charms.openstackclassesUsing
OpenStackCharmOpenStackCharm()and the related classes provide a powerful framework to build an OpenStack charm on. There are two approaches to writing charms that support multiple OpenStack releases. Note that determining the release is up to the charm author, and can be signalled toOpenStackCharmin two ways.Write a single
OpenStackCharmderived class that usesself.releaseto determine what functionality to exhibit depending on the release. In this case, there is no need to register multiple charms and provide a chooser to determine which class to use.Write multiple
OpenStackCharmderived classes which map to each difference in charm functionality depending on the release, and register a chooser function using the@register_os_release_selectordecorator.e.g.
This will automatically select
LibertyCharmfor a liberty release andMitakaCharmfor the mitaka release. Note, that it will also setreleaseon theOpenStackCharminstance via the__init__()method, so that the instance knows what the charm is.If only a single charm class is needed, the
__init__()method of the class can be used to determine the release instead:If the release selector function is registered, then the overridden
__init__()method is not needed as the release will be passed into the default__init__()method. However, there may be other functionality that the charm author needs to include in the initialiser.Note that using
os_release()can typically be used to determine the release of OpenStack.Using the relation adapter classes - OpenStackRelationAdapter
The relation adapter classes adapt a reactive interface for use in the rendering functions. Their principal use is to provide an iterator of the attributes declared in the
assessorsattribute of the instance.A reactive
BaseRelationderived instance has anauto_accessorsattribute which declares the variables that the relation has. These are copied into theaccessorsattribute of theOpenStackRelationAdapterclass, and additional attributes can be added as part of class instantiation.Note that the
accessorproperties are dynamic, in that they call the underlying relation property when they are accessed.The purpose of the
OpenStackRelationclass is for the instance to be used as part of configuration file rendering, as an instance of anOpenStackRelationclass can be passed to the render function, and the iterator will provide the key value pairs to the template processor.A derived
OpenStackRelationclass can provide additional computed properties as required. e.g. theRabbitMQRelationAdapterimplementation:Note that the additional accessors
vhostandusernameare provided in the overridden__init__()method.The
ConfigurationAdapterThe
ConfigurationAdapterclass simply provides snapshot of the configuration options for the current charm, such that they can be accessed as attributes of an instance of the class. e.g. rather thanconfig('vip')then user can usec_adapter.vip.The benefit, is that a derived version of
ConfigurationAdaptercan be provided that has computed properties that can be used like static properties on the instance. TheConfigurationAdapter, or derived class, is used with theOpenStackRelationAdaptersclass (not the plural …Adapters) class that brings together all of the relations into one place.The
OpenStackRelationAdaptersclassThe
OpenStackRelationAdaptersclass joins together the relation adapter classes, with theConfigurationAdapter(or derived) class, and works like a charmhelpersOSRenderConfiginstance to the rendering functions in charmhelpers.Thus an instance of the
OpenStackRelationAdapters(or derived) class is used in thecharmhelpers.core.templating.render()function to provide the variables needed to render templates.The
OpenStackRelationAdaptersclass can be subclassed (derived) with additional customOpenStackRelationAdapterclasses (to map to particular relations) using therelation_adaptersclass property:This enables custom relation adapters to be mapped to particular relations such that custom functionality can be implemented for a particular reactive relationship.
HighAvailability Support
To be completed.
OpenStack Upgrade via config
An OpenStack principle charm has an ‘openstack-origin’ configuration option. This is used to setup the package source for a charm. If a user updates this option to point at a package repository then the charm can be configured to automatically upgrade. This is achieved with the following steps:
Workload status
OpenStack charms support the concept of workload status which helps to inform a user of the charm of the current state of the charm. The following workload statuses are supported:
The default is for charms to support workload status, and the default installation method sets the status to maintenance with an install message.
If the charm is not going to support workload status, and this is not recommended, then the charm author will need to override the
install()method ofOpenStackCharmderived class to disable setting themaintenancestate, and override theassess_status()method to a NOP.The
assess_status()method onOpenStackCharmprovides a helper to enable the charm author to provide workload status. By default:assess_status()method is actually_assess_status()and theassess_status()method simply sets up anatexit()hook to defer the operation. This means that you can callassess_status()multiple times BUT it will actually only be invoked at the end of the charm hook execution. If you need to actually run assess_status() at the point in the handler, then call_assess_status().layer-openstacklayer provides a hook forupdate-statuswhich calls theassess_status()function on the charm class._assess_status()method uses various attributes of the class to provide a default mechanism for assessing the workload status of the charm/unit.The latter is extremely useful for determining the workload status. The
_assess_status()method does the following checks:custom_assess_status_check()Checking of relations
The assess_status function checks that the relations named in the class attribute
required_relationsare connected and available. It does this using the convention of:{relation}.connectedstate set.{relation}.availablestate set.This is a convention that the interfaces (e.g. interface-keystone, etc.) use. interface-keystone sets
identity-service.connectedwhen it has a connection with keystone, andidentity-service.availablewhen the connection is completed and all information transferred.That if
required_relationsis['identity-service'], then theassess_status()function will check foridentity-service.connectedandidentity-service.availablestates.If the charm author requires additional states to be checked for an interface, then the method
states_to_checkshould be overridden in the derived class and additional states, the status and error message provided. See the code for further details.e.g.
The
custom_assess_status_check()methodIf the charm author needs to do additional status checking, then the
custom_assess_status_check()method should be overridden in the derived class. The return value from the method is:Not checking services are running
By default, the
_assess_status()method checks that the services declared in the class attributeservices(list of strings) are checked to ensure that they are running. Additionally, the ports declared in the class attributeapi_portsare also checked for being listened on.However, if the services check is not required, then the derived class should overload the
check_running_services()method and returnNone, None.Additionally, if the services running check is required, but the ports should not be checked, then the
ports_to_checkmethod can be overridden and return an empty list[].Using
assess_status()The
assess_status()method should be used on any hook or state method where the unit’s status may have changed. e.g. interfaces connecting or becoming available, configuration changes, etc.e.g.