Workload status
This commit is contained in:
parent
1ca9cdc15a
commit
13895315bf
@ -194,10 +194,50 @@ def config_flags_parser(config_flags):
|
||||
class OSContextGenerator(object):
|
||||
"""Base class for all context generators."""
|
||||
interfaces = []
|
||||
related = False
|
||||
complete = False
|
||||
missing_data = []
|
||||
|
||||
def __call__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def context_complete(self, ctxt):
|
||||
"""Check for missing data for the required context data.
|
||||
Set self.missing_data if it exists and return False.
|
||||
Set self.complete if no missing data and return True.
|
||||
"""
|
||||
# Fresh start
|
||||
self.complete = False
|
||||
self.missing_data = []
|
||||
for k, v in six.iteritems(ctxt):
|
||||
if v is None or v == '':
|
||||
if k not in self.missing_data:
|
||||
self.missing_data.append(k)
|
||||
|
||||
if self.missing_data:
|
||||
self.complete = False
|
||||
log('Missing required data: %s' % ' '.join(self.missing_data), level=INFO)
|
||||
else:
|
||||
self.complete = True
|
||||
return self.complete
|
||||
|
||||
def get_related(self):
|
||||
"""Check if any of the context interfaces have relation ids.
|
||||
Set self.related and return True if one of the interfaces
|
||||
has relation ids.
|
||||
"""
|
||||
# Fresh start
|
||||
self.related = False
|
||||
try:
|
||||
for interface in self.interfaces:
|
||||
if relation_ids(interface):
|
||||
self.related = True
|
||||
return self.related
|
||||
except AttributeError as e:
|
||||
log("{} {}"
|
||||
"".format(self, e), 'INFO')
|
||||
return self.related
|
||||
|
||||
|
||||
class SharedDBContext(OSContextGenerator):
|
||||
interfaces = ['shared-db']
|
||||
@ -213,6 +253,7 @@ class SharedDBContext(OSContextGenerator):
|
||||
self.database = database
|
||||
self.user = user
|
||||
self.ssl_dir = ssl_dir
|
||||
self.rel_name = self.interfaces[0]
|
||||
|
||||
def __call__(self):
|
||||
self.database = self.database or config('database')
|
||||
@ -246,6 +287,7 @@ class SharedDBContext(OSContextGenerator):
|
||||
password_setting = self.relation_prefix + '_password'
|
||||
|
||||
for rid in relation_ids(self.interfaces[0]):
|
||||
self.related = True
|
||||
for unit in related_units(rid):
|
||||
rdata = relation_get(rid=rid, unit=unit)
|
||||
host = rdata.get('db_host')
|
||||
@ -257,7 +299,7 @@ class SharedDBContext(OSContextGenerator):
|
||||
'database_password': rdata.get(password_setting),
|
||||
'database_type': 'mysql'
|
||||
}
|
||||
if context_complete(ctxt):
|
||||
if self.context_complete(ctxt):
|
||||
db_ssl(rdata, ctxt, self.ssl_dir)
|
||||
return ctxt
|
||||
return {}
|
||||
@ -278,6 +320,7 @@ class PostgresqlDBContext(OSContextGenerator):
|
||||
|
||||
ctxt = {}
|
||||
for rid in relation_ids(self.interfaces[0]):
|
||||
self.related = True
|
||||
for unit in related_units(rid):
|
||||
rel_host = relation_get('host', rid=rid, unit=unit)
|
||||
rel_user = relation_get('user', rid=rid, unit=unit)
|
||||
@ -287,7 +330,7 @@ class PostgresqlDBContext(OSContextGenerator):
|
||||
'database_user': rel_user,
|
||||
'database_password': rel_passwd,
|
||||
'database_type': 'postgresql'}
|
||||
if context_complete(ctxt):
|
||||
if self.context_complete(ctxt):
|
||||
return ctxt
|
||||
|
||||
return {}
|
||||
@ -348,6 +391,7 @@ class IdentityServiceContext(OSContextGenerator):
|
||||
ctxt['signing_dir'] = cachedir
|
||||
|
||||
for rid in relation_ids(self.rel_name):
|
||||
self.related = True
|
||||
for unit in related_units(rid):
|
||||
rdata = relation_get(rid=rid, unit=unit)
|
||||
serv_host = rdata.get('service_host')
|
||||
@ -366,7 +410,7 @@ class IdentityServiceContext(OSContextGenerator):
|
||||
'service_protocol': svc_protocol,
|
||||
'auth_protocol': auth_protocol})
|
||||
|
||||
if context_complete(ctxt):
|
||||
if self.context_complete(ctxt):
|
||||
# NOTE(jamespage) this is required for >= icehouse
|
||||
# so a missing value just indicates keystone needs
|
||||
# upgrading
|
||||
@ -405,6 +449,7 @@ class AMQPContext(OSContextGenerator):
|
||||
ctxt = {}
|
||||
for rid in relation_ids(self.rel_name):
|
||||
ha_vip_only = False
|
||||
self.related = True
|
||||
for unit in related_units(rid):
|
||||
if relation_get('clustered', rid=rid, unit=unit):
|
||||
ctxt['clustered'] = True
|
||||
@ -437,7 +482,7 @@ class AMQPContext(OSContextGenerator):
|
||||
ha_vip_only = relation_get('ha-vip-only',
|
||||
rid=rid, unit=unit) is not None
|
||||
|
||||
if context_complete(ctxt):
|
||||
if self.context_complete(ctxt):
|
||||
if 'rabbit_ssl_ca' in ctxt:
|
||||
if not self.ssl_dir:
|
||||
log("Charm not setup for ssl support but ssl ca "
|
||||
@ -469,7 +514,7 @@ class AMQPContext(OSContextGenerator):
|
||||
ctxt['oslo_messaging_flags'] = config_flags_parser(
|
||||
oslo_messaging_flags)
|
||||
|
||||
if not context_complete(ctxt):
|
||||
if not self.complete:
|
||||
return {}
|
||||
|
||||
return ctxt
|
||||
@ -485,13 +530,15 @@ class CephContext(OSContextGenerator):
|
||||
|
||||
log('Generating template context for ceph', level=DEBUG)
|
||||
mon_hosts = []
|
||||
auth = None
|
||||
key = None
|
||||
use_syslog = str(config('use-syslog')).lower()
|
||||
ctxt = {
|
||||
'use_syslog': str(config('use-syslog')).lower()
|
||||
}
|
||||
for rid in relation_ids('ceph'):
|
||||
for unit in related_units(rid):
|
||||
auth = relation_get('auth', rid=rid, unit=unit)
|
||||
key = relation_get('key', rid=rid, unit=unit)
|
||||
if not ctxt.get('auth'):
|
||||
ctxt['auth'] = relation_get('auth', rid=rid, unit=unit)
|
||||
if not ctxt.get('key'):
|
||||
ctxt['key'] = relation_get('key', rid=rid, unit=unit)
|
||||
ceph_pub_addr = relation_get('ceph-public-address', rid=rid,
|
||||
unit=unit)
|
||||
unit_priv_addr = relation_get('private-address', rid=rid,
|
||||
@ -500,15 +547,12 @@ class CephContext(OSContextGenerator):
|
||||
ceph_addr = format_ipv6_addr(ceph_addr) or ceph_addr
|
||||
mon_hosts.append(ceph_addr)
|
||||
|
||||
ctxt = {'mon_hosts': ' '.join(sorted(mon_hosts)),
|
||||
'auth': auth,
|
||||
'key': key,
|
||||
'use_syslog': use_syslog}
|
||||
ctxt['mon_hosts'] = ' '.join(sorted(mon_hosts))
|
||||
|
||||
if not os.path.isdir('/etc/ceph'):
|
||||
os.mkdir('/etc/ceph')
|
||||
|
||||
if not context_complete(ctxt):
|
||||
if not self.context_complete(ctxt):
|
||||
return {}
|
||||
|
||||
ensure_packages(['ceph-common'])
|
||||
@ -1367,6 +1411,6 @@ class NetworkServiceContext(OSContextGenerator):
|
||||
'auth_protocol':
|
||||
rdata.get('auth_protocol') or 'http',
|
||||
}
|
||||
if context_complete(ctxt):
|
||||
if self.context_complete(ctxt):
|
||||
return ctxt
|
||||
return {}
|
||||
|
@ -18,7 +18,7 @@ import os
|
||||
|
||||
import six
|
||||
|
||||
from charmhelpers.fetch import apt_install
|
||||
from charmhelpers.fetch import apt_install, apt_update
|
||||
from charmhelpers.core.hookenv import (
|
||||
log,
|
||||
ERROR,
|
||||
@ -29,6 +29,7 @@ from charmhelpers.contrib.openstack.utils import OPENSTACK_CODENAMES
|
||||
try:
|
||||
from jinja2 import FileSystemLoader, ChoiceLoader, Environment, exceptions
|
||||
except ImportError:
|
||||
apt_update(fatal=True)
|
||||
apt_install('python-jinja2', fatal=True)
|
||||
from jinja2 import FileSystemLoader, ChoiceLoader, Environment, exceptions
|
||||
|
||||
@ -112,7 +113,7 @@ class OSConfigTemplate(object):
|
||||
|
||||
def complete_contexts(self):
|
||||
'''
|
||||
Return a list of interfaces that have atisfied contexts.
|
||||
Return a list of interfaces that have satisfied contexts.
|
||||
'''
|
||||
if self._complete_contexts:
|
||||
return self._complete_contexts
|
||||
@ -293,3 +294,30 @@ class OSConfigRenderer(object):
|
||||
[interfaces.extend(i.complete_contexts())
|
||||
for i in six.itervalues(self.templates)]
|
||||
return interfaces
|
||||
|
||||
def get_incomplete_context_data(self, interfaces):
|
||||
'''
|
||||
Return dictionary of relation status of interfaces and any missing
|
||||
required context data. Example:
|
||||
{'amqp': {'missing_data': ['rabbitmq_password'], 'related': True},
|
||||
'zeromq-configuration': {'related': False}}
|
||||
'''
|
||||
incomplete_context_data = {}
|
||||
|
||||
for i in six.itervalues(self.templates):
|
||||
for context in i.contexts:
|
||||
for interface in interfaces:
|
||||
related = False
|
||||
if interface in context.interfaces:
|
||||
related = context.get_related()
|
||||
missing_data = context.missing_data
|
||||
if missing_data:
|
||||
incomplete_context_data[interface] = {'missing_data': missing_data}
|
||||
if related:
|
||||
if incomplete_context_data.get(interface):
|
||||
incomplete_context_data[interface].update({'related': True})
|
||||
else:
|
||||
incomplete_context_data[interface] = {'related': True}
|
||||
else:
|
||||
incomplete_context_data[interface] = {'related': False}
|
||||
return incomplete_context_data
|
||||
|
@ -25,6 +25,7 @@ import sys
|
||||
import re
|
||||
|
||||
import six
|
||||
import traceback
|
||||
import yaml
|
||||
|
||||
from charmhelpers.contrib.network import ip
|
||||
@ -34,12 +35,16 @@ from charmhelpers.core import (
|
||||
)
|
||||
|
||||
from charmhelpers.core.hookenv import (
|
||||
action_fail,
|
||||
action_set,
|
||||
config,
|
||||
log as juju_log,
|
||||
charm_dir,
|
||||
INFO,
|
||||
relation_ids,
|
||||
relation_set
|
||||
relation_set,
|
||||
status_set,
|
||||
hook_name
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.storage.linux.lvm import (
|
||||
@ -114,6 +119,7 @@ SWIFT_CODENAMES = OrderedDict([
|
||||
('2.2.1', 'kilo'),
|
||||
('2.2.2', 'kilo'),
|
||||
('2.3.0', 'liberty'),
|
||||
('2.4.0', 'liberty'),
|
||||
])
|
||||
|
||||
# >= Liberty version->codename mapping
|
||||
@ -142,6 +148,9 @@ PACKAGE_CODENAMES = {
|
||||
'glance-common': OrderedDict([
|
||||
('11.0.0', 'liberty'),
|
||||
]),
|
||||
'openstack-dashboard': OrderedDict([
|
||||
('8.0.0', 'liberty'),
|
||||
]),
|
||||
}
|
||||
|
||||
DEFAULT_LOOPBACK_SIZE = '5G'
|
||||
@ -745,3 +754,217 @@ def git_yaml_value(projects_yaml, key):
|
||||
return projects[key]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def os_workload_status(configs, required_interfaces, charm_func=None):
|
||||
"""
|
||||
Decorator to set workload status based on complete contexts
|
||||
"""
|
||||
def wrap(f):
|
||||
@wraps(f)
|
||||
def wrapped_f(*args, **kwargs):
|
||||
# Run the original function first
|
||||
f(*args, **kwargs)
|
||||
# Set workload status now that contexts have been
|
||||
# acted on
|
||||
set_os_workload_status(configs, required_interfaces, charm_func)
|
||||
return wrapped_f
|
||||
return wrap
|
||||
|
||||
|
||||
def set_os_workload_status(configs, required_interfaces, charm_func=None):
|
||||
"""
|
||||
Set workload status based on complete contexts.
|
||||
status-set missing or incomplete contexts
|
||||
and juju-log details of missing required data.
|
||||
charm_func is a charm specific function to run checking
|
||||
for charm specific requirements such as a VIP setting.
|
||||
"""
|
||||
incomplete_rel_data = incomplete_relation_data(configs, required_interfaces)
|
||||
state = 'active'
|
||||
missing_relations = []
|
||||
incomplete_relations = []
|
||||
message = None
|
||||
charm_state = None
|
||||
charm_message = None
|
||||
|
||||
for generic_interface in incomplete_rel_data.keys():
|
||||
related_interface = None
|
||||
missing_data = {}
|
||||
# Related or not?
|
||||
for interface in incomplete_rel_data[generic_interface]:
|
||||
if incomplete_rel_data[generic_interface][interface].get('related'):
|
||||
related_interface = interface
|
||||
missing_data = incomplete_rel_data[generic_interface][interface].get('missing_data')
|
||||
# No relation ID for the generic_interface
|
||||
if not related_interface:
|
||||
juju_log("{} relation is missing and must be related for "
|
||||
"functionality. ".format(generic_interface), 'WARN')
|
||||
state = 'blocked'
|
||||
if generic_interface not in missing_relations:
|
||||
missing_relations.append(generic_interface)
|
||||
else:
|
||||
# Relation ID exists but no related unit
|
||||
if not missing_data:
|
||||
# Edge case relation ID exists but departing
|
||||
if ('departed' in hook_name() or 'broken' in hook_name()) \
|
||||
and related_interface in hook_name():
|
||||
state = 'blocked'
|
||||
if generic_interface not in missing_relations:
|
||||
missing_relations.append(generic_interface)
|
||||
juju_log("{} relation's interface, {}, "
|
||||
"relationship is departed or broken "
|
||||
"and is required for functionality."
|
||||
"".format(generic_interface, related_interface), "WARN")
|
||||
# Normal case relation ID exists but no related unit
|
||||
# (joining)
|
||||
else:
|
||||
juju_log("{} relations's interface, {}, is related but has "
|
||||
"no units in the relation."
|
||||
"".format(generic_interface, related_interface), "INFO")
|
||||
# Related unit exists and data missing on the relation
|
||||
else:
|
||||
juju_log("{} relation's interface, {}, is related awaiting "
|
||||
"the following data from the relationship: {}. "
|
||||
"".format(generic_interface, related_interface,
|
||||
", ".join(missing_data)), "INFO")
|
||||
if state != 'blocked':
|
||||
state = 'waiting'
|
||||
if generic_interface not in incomplete_relations \
|
||||
and generic_interface not in missing_relations:
|
||||
incomplete_relations.append(generic_interface)
|
||||
|
||||
if missing_relations:
|
||||
message = "Missing relations: {}".format(", ".join(missing_relations))
|
||||
if incomplete_relations:
|
||||
message += "; incomplete relations: {}" \
|
||||
"".format(", ".join(incomplete_relations))
|
||||
state = 'blocked'
|
||||
elif incomplete_relations:
|
||||
message = "Incomplete relations: {}" \
|
||||
"".format(", ".join(incomplete_relations))
|
||||
state = 'waiting'
|
||||
|
||||
# Run charm specific checks
|
||||
if charm_func:
|
||||
charm_state, charm_message = charm_func(configs)
|
||||
if charm_state != 'active' and charm_state != 'unknown':
|
||||
state = workload_state_compare(state, charm_state)
|
||||
if message:
|
||||
message = "{} {}".format(message, charm_message)
|
||||
else:
|
||||
message = charm_message
|
||||
|
||||
# Set to active if all requirements have been met
|
||||
if state == 'active':
|
||||
message = "Unit is ready"
|
||||
juju_log(message, "INFO")
|
||||
|
||||
status_set(state, message)
|
||||
|
||||
|
||||
def workload_state_compare(current_workload_state, workload_state):
|
||||
""" Return highest priority of two states"""
|
||||
hierarchy = {'unknown': -1,
|
||||
'active': 0,
|
||||
'maintenance': 1,
|
||||
'waiting': 2,
|
||||
'blocked': 3,
|
||||
}
|
||||
|
||||
if hierarchy.get(workload_state) is None:
|
||||
workload_state = 'unknown'
|
||||
if hierarchy.get(current_workload_state) is None:
|
||||
current_workload_state = 'unknown'
|
||||
|
||||
# Set workload_state based on hierarchy of statuses
|
||||
if hierarchy.get(current_workload_state) > hierarchy.get(workload_state):
|
||||
return current_workload_state
|
||||
else:
|
||||
return workload_state
|
||||
|
||||
|
||||
def incomplete_relation_data(configs, required_interfaces):
|
||||
"""
|
||||
Check complete contexts against required_interfaces
|
||||
Return dictionary of incomplete relation data.
|
||||
|
||||
configs is an OSConfigRenderer object with configs registered
|
||||
|
||||
required_interfaces is a dictionary of required general interfaces
|
||||
with dictionary values of possible specific interfaces.
|
||||
Example:
|
||||
required_interfaces = {'database': ['shared-db', 'pgsql-db']}
|
||||
|
||||
The interface is said to be satisfied if anyone of the interfaces in the
|
||||
list has a complete context.
|
||||
|
||||
Return dictionary of incomplete or missing required contexts with relation
|
||||
status of interfaces and any missing data points. Example:
|
||||
{'message':
|
||||
{'amqp': {'missing_data': ['rabbitmq_password'], 'related': True},
|
||||
'zeromq-configuration': {'related': False}},
|
||||
'identity':
|
||||
{'identity-service': {'related': False}},
|
||||
'database':
|
||||
{'pgsql-db': {'related': False},
|
||||
'shared-db': {'related': True}}}
|
||||
"""
|
||||
complete_ctxts = configs.complete_contexts()
|
||||
incomplete_relations = []
|
||||
for svc_type in required_interfaces.keys():
|
||||
# Avoid duplicates
|
||||
found_ctxt = False
|
||||
for interface in required_interfaces[svc_type]:
|
||||
if interface in complete_ctxts:
|
||||
found_ctxt = True
|
||||
if not found_ctxt:
|
||||
incomplete_relations.append(svc_type)
|
||||
incomplete_context_data = {}
|
||||
for i in incomplete_relations:
|
||||
incomplete_context_data[i] = configs.get_incomplete_context_data(required_interfaces[i])
|
||||
return incomplete_context_data
|
||||
|
||||
|
||||
def do_action_openstack_upgrade(package, upgrade_callback, configs):
|
||||
"""Perform action-managed OpenStack upgrade.
|
||||
|
||||
Upgrades packages to the configured openstack-origin version and sets
|
||||
the corresponding action status as a result.
|
||||
|
||||
If the charm was installed from source we cannot upgrade it.
|
||||
For backwards compatibility a config flag (action-managed-upgrade) must
|
||||
be set for this code to run, otherwise a full service level upgrade will
|
||||
fire on config-changed.
|
||||
|
||||
@param package: package name for determining if upgrade available
|
||||
@param upgrade_callback: function callback to charm's upgrade function
|
||||
@param configs: templating object derived from OSConfigRenderer class
|
||||
|
||||
@return: True if upgrade successful; False if upgrade failed or skipped
|
||||
"""
|
||||
ret = False
|
||||
|
||||
if git_install_requested():
|
||||
action_set({'outcome': 'installed from source, skipped upgrade.'})
|
||||
else:
|
||||
if openstack_upgrade_available(package):
|
||||
if config('action-managed-upgrade'):
|
||||
juju_log('Upgrading OpenStack release')
|
||||
|
||||
try:
|
||||
upgrade_callback(configs=configs)
|
||||
action_set({'outcome': 'success, upgrade completed.'})
|
||||
ret = True
|
||||
except:
|
||||
action_set({'outcome': 'upgrade failed, see traceback.'})
|
||||
action_set({'traceback': traceback.format_exc()})
|
||||
action_fail('do_openstack_upgrade resulted in an '
|
||||
'unexpected error')
|
||||
else:
|
||||
action_set({'outcome': 'action-managed-upgrade config is '
|
||||
'False, skipped upgrade.'})
|
||||
else:
|
||||
action_set({'outcome': 'no upgrade available.'})
|
||||
|
||||
return ret
|
||||
|
@ -24,6 +24,7 @@ from charmhelpers.core.hookenv import (
|
||||
relation_set,
|
||||
related_units,
|
||||
unit_get,
|
||||
status_set,
|
||||
)
|
||||
|
||||
from charmhelpers.core.host import (
|
||||
@ -45,7 +46,8 @@ from charmhelpers.contrib.openstack.utils import (
|
||||
configure_installation_source,
|
||||
git_install_requested,
|
||||
openstack_upgrade_available,
|
||||
sync_db_with_multi_ipv6_addresses
|
||||
sync_db_with_multi_ipv6_addresses,
|
||||
os_workload_status,
|
||||
)
|
||||
|
||||
from keystone_utils import (
|
||||
@ -80,6 +82,8 @@ from keystone_utils import (
|
||||
force_ssl_sync,
|
||||
filter_null,
|
||||
ensure_ssl_dirs,
|
||||
REQUIRED_INTERFACES,
|
||||
check_ha_settings,
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.hahelpers.cluster import (
|
||||
@ -113,20 +117,26 @@ CONFIGS = register_configs()
|
||||
|
||||
|
||||
@hooks.hook('install.real')
|
||||
@os_workload_status(CONFIGS, REQUIRED_INTERFACES, charm_func=check_ha_settings)
|
||||
def install():
|
||||
status_set('maintenance', 'Executing pre-install')
|
||||
execd_preinstall()
|
||||
configure_installation_source(config('openstack-origin'))
|
||||
status_set('maintenance', 'Installing apt packages')
|
||||
apt_update()
|
||||
apt_install(determine_packages(), fatal=True)
|
||||
|
||||
status_set('maintenance', 'Git install')
|
||||
git_install(config('openstack-origin-git'))
|
||||
|
||||
|
||||
@hooks.hook('config-changed')
|
||||
@os_workload_status(CONFIGS, REQUIRED_INTERFACES, charm_func=check_ha_settings)
|
||||
@restart_on_change(restart_map())
|
||||
@synchronize_ca_if_changed(fatal=True)
|
||||
def config_changed():
|
||||
if config('prefer-ipv6'):
|
||||
status_set('maintenance', 'configuring ipv6')
|
||||
setup_ipv6()
|
||||
sync_db_with_multi_ipv6_addresses(config('database'),
|
||||
config('database-user'))
|
||||
@ -139,9 +149,11 @@ def config_changed():
|
||||
|
||||
if git_install_requested():
|
||||
if config_value_changed('openstack-origin-git'):
|
||||
status_set('maintenance', 'Running Git install')
|
||||
git_install(config('openstack-origin-git'))
|
||||
else:
|
||||
if openstack_upgrade_available('keystone'):
|
||||
status_set('maintenance', 'Running openstack upgrade')
|
||||
do_openstack_upgrade(configs=CONFIGS)
|
||||
|
||||
# Ensure ssl dir exists and is unison-accessible
|
||||
@ -196,6 +208,7 @@ def initialise_pki():
|
||||
|
||||
|
||||
@hooks.hook('shared-db-relation-joined')
|
||||
@os_workload_status(CONFIGS, REQUIRED_INTERFACES, charm_func=check_ha_settings)
|
||||
def db_joined():
|
||||
if is_relation_made('pgsql-db'):
|
||||
# error, postgresql is used
|
||||
@ -214,6 +227,7 @@ def db_joined():
|
||||
|
||||
|
||||
@hooks.hook('pgsql-db-relation-joined')
|
||||
@os_workload_status(CONFIGS, REQUIRED_INTERFACES, charm_func=check_ha_settings)
|
||||
def pgsql_db_joined():
|
||||
if is_relation_made('shared-db'):
|
||||
# raise error
|
||||
@ -252,6 +266,7 @@ def update_all_identity_relation_units_force_sync():
|
||||
|
||||
|
||||
@hooks.hook('shared-db-relation-changed')
|
||||
@os_workload_status(CONFIGS, REQUIRED_INTERFACES, charm_func=check_ha_settings)
|
||||
@restart_on_change(restart_map())
|
||||
@synchronize_ca_if_changed()
|
||||
def db_changed():
|
||||
@ -275,6 +290,7 @@ def db_changed():
|
||||
|
||||
|
||||
@hooks.hook('pgsql-db-relation-changed')
|
||||
@os_workload_status(CONFIGS, REQUIRED_INTERFACES, charm_func=check_ha_settings)
|
||||
@restart_on_change(restart_map())
|
||||
@synchronize_ca_if_changed()
|
||||
def pgsql_db_changed():
|
||||
@ -474,6 +490,7 @@ def leader_settings_changed():
|
||||
|
||||
|
||||
@hooks.hook('ha-relation-joined')
|
||||
@os_workload_status(CONFIGS, REQUIRED_INTERFACES, charm_func=check_ha_settings)
|
||||
def ha_joined(relation_id=None):
|
||||
cluster_config = get_hacluster_config()
|
||||
resources = {
|
||||
@ -531,6 +548,7 @@ def ha_joined(relation_id=None):
|
||||
@hooks.hook('ha-relation-changed')
|
||||
@restart_on_change(restart_map())
|
||||
@synchronize_ca_if_changed()
|
||||
@os_workload_status(CONFIGS, REQUIRED_INTERFACES, charm_func=check_ha_settings)
|
||||
def ha_changed():
|
||||
CONFIGS.write_all()
|
||||
|
||||
@ -576,6 +594,7 @@ def configure_https():
|
||||
@restart_on_change(restart_map(), stopstart=True)
|
||||
@synchronize_ca_if_changed()
|
||||
def upgrade_charm():
|
||||
status_set('maintenance', 'Installing apt packages')
|
||||
apt_install(filter_installed_packages(determine_packages()))
|
||||
unison.ssh_authorized_peers(user=SSH_USER,
|
||||
group='juju_keystone',
|
||||
|
@ -24,6 +24,7 @@ from charmhelpers.contrib.hahelpers.cluster import(
|
||||
determine_api_port,
|
||||
https,
|
||||
peer_units,
|
||||
get_hacluster_config,
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.openstack import context, templating
|
||||
@ -271,6 +272,12 @@ valid_services = {
|
||||
}
|
||||
}
|
||||
|
||||
# The interface is said to be satisfied if anyone of the interfaces in the
|
||||
# list has a complete context.
|
||||
REQUIRED_INTERFACES = {
|
||||
'database': ['shared-db', 'pgsql-db'],
|
||||
}
|
||||
|
||||
|
||||
def filter_null(settings, null='__null__'):
|
||||
"""Replace null values with None in provided settings dict.
|
||||
@ -1775,3 +1782,16 @@ def git_post_install(projects_yaml):
|
||||
perms=0o644, templates_dir=templates_dir)
|
||||
|
||||
service_restart('keystone')
|
||||
|
||||
|
||||
def check_ha_settings(configs):
|
||||
if relation_ids('ha'):
|
||||
try:
|
||||
get_hacluster_config()
|
||||
return 'active', 'hacluster configs complete.'
|
||||
except:
|
||||
return ('blocked',
|
||||
'hacluster missing configuration: '
|
||||
'vip, vip_iface, vip_cidr')
|
||||
else:
|
||||
return 'unknown', 'No ha clustering'
|
||||
|
@ -6,6 +6,9 @@ import yaml
|
||||
from contextlib import contextmanager
|
||||
from mock import patch, MagicMock
|
||||
|
||||
patch('charmhelpers.contrib.openstack.utils.set_os_workload_status').start()
|
||||
patch('charmhelpers.core.hookenv.status_set').start()
|
||||
|
||||
|
||||
def load_config():
|
||||
'''Walk backwords from __file__ looking for config.yaml,
|
||||
|
Loading…
Reference in New Issue
Block a user