226 lines
7.9 KiB
Python
226 lines
7.9 KiB
Python
import charmhelpers.contrib.openstack.utils as os_utils
|
|
import charmhelpers.core.hookenv as hookenv
|
|
import charmhelpers.core.unitdata as unitdata
|
|
import charms.reactive as reactive
|
|
|
|
from charms_openstack.charm.classes import OpenStackCharm
|
|
from charms_openstack.charm.core import register_os_release_selector
|
|
|
|
# The default handlers that charms.openstack provides.
|
|
ALLOWED_DEFAULT_HANDLERS = [
|
|
'charm.installed',
|
|
'amqp.connected',
|
|
'shared-db.connected',
|
|
'identity-service.connected',
|
|
'identity-service.available',
|
|
'config.changed',
|
|
'charm.default-select-release',
|
|
'update-status',
|
|
'upgrade-charm',
|
|
]
|
|
|
|
# Where to store the default handler functions for each default state
|
|
_default_handler_map = {}
|
|
|
|
# Used to store the discovered release version for caching between invocations
|
|
OPENSTACK_RELEASE_KEY = 'charmers.openstack-release-version'
|
|
|
|
|
|
def use_defaults(*defaults):
|
|
"""Activate the default functionality for various handlers
|
|
|
|
This is to provide default functionality for common operations for
|
|
openstack charms.
|
|
"""
|
|
for state in defaults:
|
|
if state in ALLOWED_DEFAULT_HANDLERS:
|
|
if state in _default_handler_map:
|
|
# Initialise the default handler for this state
|
|
_default_handler_map[state]()
|
|
else:
|
|
raise RuntimeError(
|
|
"State '{}' is allowed, but has no handler???"
|
|
.format(state))
|
|
else:
|
|
raise RuntimeError("Default handler for '{}' doesn't exist"
|
|
.format(state))
|
|
|
|
|
|
def _map_default_handler(state):
|
|
"""Decorator to map a default handler to a state -- just makes adding
|
|
handlers a bit easier.
|
|
|
|
:param state: the state that the handler is for.
|
|
:raises RuntimeError: if the state doesn't exist in
|
|
ALLOWED_DEFAULT_HANDLERS
|
|
"""
|
|
def wrapper(f):
|
|
if state in _default_handler_map:
|
|
raise RuntimeError(
|
|
"State '{}' can't have more than one default handler"
|
|
.format(state))
|
|
if state not in ALLOWED_DEFAULT_HANDLERS:
|
|
raise RuntimeError(
|
|
"State '{} doesn't have a default handler????".format(state))
|
|
_default_handler_map[state] = f
|
|
return f
|
|
return wrapper
|
|
|
|
|
|
@_map_default_handler('charm.installed')
|
|
def make_default_install_handler():
|
|
|
|
@reactive.when_not('charm.installed')
|
|
def default_install():
|
|
"""Provide a default install handler
|
|
|
|
The instance automagically becomes the derived OpenStackCharm instance.
|
|
The kv() key charmers.openstack-release-version' is used to cache the
|
|
release being used for this charm. It is determined by the
|
|
default_select_release() function below, unless this is overriden by
|
|
the charm author
|
|
"""
|
|
unitdata.kv().unset(OPENSTACK_RELEASE_KEY)
|
|
OpenStackCharm.singleton.install()
|
|
reactive.set_state('charm.installed')
|
|
|
|
|
|
@_map_default_handler('charm.default-select-release')
|
|
def make_default_select_release_handler():
|
|
"""This handler is a bit more unusual, as it just sets the release selector
|
|
using the @register_os_release_selector decorator
|
|
"""
|
|
|
|
@register_os_release_selector
|
|
def default_select_release():
|
|
"""Determine the release based on the python-keystonemiddleware that is
|
|
installed.
|
|
|
|
Note that this function caches the release after the first install so
|
|
that it doesn't need to keep going and getting it from the package
|
|
information.
|
|
"""
|
|
release_version = unitdata.kv().get(OPENSTACK_RELEASE_KEY, None)
|
|
if release_version is None:
|
|
release_version = os_utils.os_release('python-keystonemiddleware')
|
|
unitdata.kv().set(OPENSTACK_RELEASE_KEY, release_version)
|
|
return release_version
|
|
|
|
|
|
@_map_default_handler('amqp.connected')
|
|
def make_default_amqp_connection_handler():
|
|
|
|
@reactive.when('amqp.connected')
|
|
def default_amqp_connection(amqp):
|
|
"""Handle the default amqp connection.
|
|
|
|
This requires that the charm implements get_amqp_credentials() to
|
|
provide a tuple of the (user, vhost) for the amqp server
|
|
"""
|
|
instance = OpenStackCharm.singleton
|
|
user, vhost = instance.get_amqp_credentials()
|
|
amqp.request_access(username=user, vhost=vhost)
|
|
instance.assess_status()
|
|
|
|
|
|
@_map_default_handler('shared-db.connected')
|
|
def make_default_setup_database_handler():
|
|
|
|
@reactive.when('shared-db.connected')
|
|
def default_setup_database(database):
|
|
"""Handle the default database connection setup
|
|
|
|
This requires that the charm implements get_database_setup() to provide
|
|
a list of dictionaries;
|
|
[{'database': ..., 'username': ..., 'hostname': ..., 'prefix': ...}]
|
|
|
|
The prefix can be missing: it defaults to None.
|
|
"""
|
|
instance = OpenStackCharm.singleton
|
|
for db in instance.get_database_setup():
|
|
database.configure(**db)
|
|
instance.assess_status()
|
|
|
|
|
|
@_map_default_handler('identity-service.connected')
|
|
def make_default_setup_endpoint_connection():
|
|
|
|
@reactive.when('identity-service.connected')
|
|
def default_setup_endpoint_connection(keystone):
|
|
"""When the keystone interface connects, register this unit into the
|
|
catalog. This is the default handler, and calls on the charm class to
|
|
provide the endpoint information. If multiple endpoints are needed,
|
|
then a custom endpoint handler will be needed.
|
|
"""
|
|
instance = OpenStackCharm.singleton
|
|
keystone.register_endpoints(instance.service_type,
|
|
instance.region,
|
|
instance.public_url,
|
|
instance.internal_url,
|
|
instance.admin_url)
|
|
instance.assess_status()
|
|
|
|
|
|
@_map_default_handler('identity-service.available')
|
|
def make_setup_endpoint_available_handler():
|
|
|
|
@reactive.when('identity-service.available')
|
|
def default_setup_endpoint_available(keystone):
|
|
"""When the identity-service interface is available, this default
|
|
handler switches on the SSL support.
|
|
"""
|
|
instance = OpenStackCharm.singleton
|
|
instance.configure_ssl(keystone)
|
|
instance.assess_status()
|
|
|
|
|
|
@_map_default_handler('config.changed')
|
|
def make_default_config_changed_handler():
|
|
|
|
@reactive.when('config.changed')
|
|
def default_config_changed():
|
|
"""Default handler for config.changed state from reactive. Just see if
|
|
our status has changed. This is just to clear any errors that may have
|
|
got stuck due to missing async handlers, etc.
|
|
"""
|
|
instance = OpenStackCharm.singleton
|
|
instance.config_changed()
|
|
instance.assess_status()
|
|
|
|
|
|
@_map_default_handler('upgrade-charm')
|
|
def make_default_upgrade_charm_handler():
|
|
|
|
@reactive.hook('upgrade-charm')
|
|
def default_upgrade_charm():
|
|
"""Default handler for the 'upgrade-charm' hook.
|
|
This calls the charm.singleton.upgrade_charm() function as a default.
|
|
"""
|
|
OpenStackCharm.singleton.upgrade_charm()
|
|
|
|
|
|
def default_render_configs(*interfaces):
|
|
"""Default renderer for configurations. Really just a proxy for
|
|
OpenstackCharm.singleton.render_configs(..) with a call to update the
|
|
workload status afterwards.
|
|
|
|
:params *interfaces: the list of interfaces to provide to the
|
|
render_configs() function
|
|
"""
|
|
instance = OpenStackCharm.singleton
|
|
instance.render_configs(interfaces)
|
|
instance.assess_status()
|
|
|
|
|
|
@_map_default_handler('update-status')
|
|
def make_default_update_status_handler():
|
|
|
|
@reactive.hook('update-status')
|
|
def default_update_status():
|
|
"""Default handler for update-status state.
|
|
Just call update status.
|
|
"""
|
|
instance = OpenStackCharm.singleton
|
|
hookenv.application_version_set(instance.application_version)
|
|
instance.assess_status()
|