charm-interface-mysql-shared/requires.py

214 lines
7.0 KiB
Python

from charmhelpers.core import hookenv
from charms.reactive import RelationBase
from charms.reactive import hook
from charms.reactive import scopes
class MySQLSharedRequires(RelationBase):
scope = scopes.GLOBAL
# These remote data fields will be automatically mapped to accessors
# with a basic documentation string provided.
auto_accessors = ['access-network', 'db_host', 'db_port',
'ssl_ca', 'ssl_cert', 'ssl_key',
'cluster-series-upgrading', 'wait_timeout']
@hook('{requires:mysql-shared}-relation-joined')
def joined(self):
self.set_state('{relation_name}.connected')
@hook('{requires:mysql-shared}-relation-changed')
def changed(self):
if self.cluster_series_upgrading() == 'True':
self.remove_state('{relation_name}.available')
self.remove_state('{relation_name}.available.access_network')
self.remove_state('{relation_name}.available.ssl')
else:
if self.base_data_complete() and self.unit_allowed_all_dbs():
self.set_state('{relation_name}.available')
if self.access_network_data_complete():
self.set_state('{relation_name}.available.access_network')
if self.ssl_data_complete():
self.set_state('{relation_name}.available.ssl')
@hook('{requires:mysql-shared}-relation-{broken,departed}')
def departed(self):
# Clear state
self.remove_state('{relation_name}.connected')
self.remove_state('{relation_name}.available')
self.remove_state('{relation_name}.available.access_network')
self.remove_state('{relation_name}.available.ssl')
# Check if this is the last unit
for conversation in self.conversations():
for rel_id in conversation.relation_ids:
if len(hookenv.related_units(rel_id)) > 0:
# This is not the last unit so reevaluate state
self.joined()
self.changed()
def configure(self, database, username, hostname=None, prefix=None):
"""
Called by charm layer that uses this interface to configure a database.
"""
if not hostname:
conversation = self.conversation()
try:
hostname = hookenv.network_get_primary_address(
conversation.relation_name
)
except NotImplementedError:
hostname = hookenv.unit_private_ip()
if prefix:
relation_info = {
prefix + '_database': database,
prefix + '_username': username,
prefix + '_hostname': hostname,
}
self.set_prefix(prefix)
else:
relation_info = {
'database': database,
'username': username,
'hostname': hostname,
}
self.set_remote(**relation_info)
self.set_local(**relation_info)
def set_prefix(self, prefix):
"""
Store all of the database prefixes in a list.
"""
prefixes = self.get_local('prefixes')
if prefixes:
if prefix not in prefixes:
self.set_local('prefixes', prefixes + [prefix])
else:
self.set_local('prefixes', [prefix])
def get_prefixes(self):
"""
Return the list of saved prefixes.
"""
return self.get_local('prefixes')
def database(self, prefix=None):
"""
Return a configured database name.
"""
if prefix:
return self.get_local(prefix + '_database')
return self.get_local('database')
def username(self, prefix=None):
"""
Return a configured username.
"""
if prefix:
return self.get_local(prefix + '_username')
return self.get_local('username')
def hostname(self, prefix=None):
"""
Return a configured hostname.
"""
if prefix:
return self.get_local(prefix + '_hostname')
return self.get_local('hostname')
def password(self, prefix=None):
"""
Return a database password.
"""
if prefix:
return self.get_remote(prefix + '_password')
return self.get_remote('password')
def allowed_units(self, prefix=None):
"""
Return a database's allowed_units.
"""
if prefix:
return self.get_remote(prefix + '_allowed_units')
return self.get_remote('allowed_units')
def base_data_complete(self):
"""
Check if required base data is complete.
"""
data = {
'db_host': self.db_host(),
}
if self.get_prefixes():
suffixes = ['_password', '_allowed_units']
for prefix in self.get_prefixes():
for suffix in suffixes:
key = prefix + suffix
data[key] = self.get_remote(key)
else:
data['password'] = self.get_remote('password')
data['allowed_units'] = self.get_remote('allowed_units')
if all(data.values()):
return True
return False
def unit_allowed_db(self, prefix=None):
""""
Check unit can access requested database.
:param prefix: Prefix used to distinguish multiple db requests.
:type prefix: str
:returns: Whether db acl has been setup.
:rtype: bool
"""
allowed = False
allowed_units = self.allowed_units(prefix=prefix) or ''
hookenv.log("Checking {} is in {}".format(
hookenv.local_unit(),
allowed_units.split()))
if allowed_units and hookenv.local_unit() in allowed_units.split():
allowed = True
hookenv.log("Unit allowed: {}".format(allowed))
return allowed
def unit_allowed_all_dbs(self):
""""
Check unit can access all requested databases.
:returns: Whether db acl has been setup for all dbs.
:rtype: bool
"""
if self.get_prefixes():
_allowed = [self.unit_allowed_db(prefix=p)
for p in self.get_prefixes()]
else:
_allowed = [self.unit_allowed_db()]
hookenv.log("Allowed: {}".format(_allowed))
if all(_allowed):
hookenv.log("Returning unit_allowed_all_dbs True")
return True
hookenv.log("Returning unit_allowed_all_dbs False")
return False
def access_network_data_complete(self):
"""
Check if optional access network data provided by mysql is complete.
"""
data = {
'access-network': self.access_network(),
}
if all(data.values()):
return True
return False
def ssl_data_complete(self):
"""
Check if optional ssl data provided by mysql is complete.
"""
data = {
'ssl_ca': self.ssl_ca(),
}
if all(data.values()):
return True
return False