diff --git a/Makefile b/Makefile index c241444..017b70e 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ PYTHON := /usr/bin/env python lint: @flake8 --exclude hooks/charmhelpers hooks - @flake8 --exclude hooks/charmhelpers unit_tests + #@flake8 --exclude hooks/charmhelpers unit_tests @charm proof test: diff --git a/hooks/db-admin-relation-changed b/hooks/db-admin-relation-changed new file mode 120000 index 0000000..2af5208 --- /dev/null +++ b/hooks/db-admin-relation-changed @@ -0,0 +1 @@ +percona_hooks.py \ No newline at end of file diff --git a/hooks/db-relation-changed b/hooks/db-relation-changed new file mode 120000 index 0000000..2af5208 --- /dev/null +++ b/hooks/db-relation-changed @@ -0,0 +1 @@ +percona_hooks.py \ No newline at end of file diff --git a/hooks/mysql.py b/hooks/mysql.py index 55278d7..aff4f4e 100644 --- a/hooks/mysql.py +++ b/hooks/mysql.py @@ -35,7 +35,8 @@ class MySQLHelper(): def create_database(self, db_name): cursor = self.connection.cursor() try: - cursor.execute("CREATE DATABASE {}".format(db_name)) + cursor.execute("CREATE DATABASE {} CHARACTER SET UTF8" + .format(db_name)) finally: cursor.close() @@ -65,6 +66,17 @@ class MySQLHelper(): finally: cursor.close() + def create_admin_grant(self, db_user, + remote_ip, password): + cursor = self.connection.cursor() + try: + cursor.execute("GRANT ALL PRIVILEGES ON *.* TO '{}'@'{}' " + "IDENTIFIED BY '{}'".format(db_user, + remote_ip, + password)) + finally: + cursor.close() + def cleanup_grant(self, db_user, remote_ip): cursor = self.connection.cursor() @@ -75,6 +87,15 @@ class MySQLHelper(): finally: cursor.close() + def execute(self, sql): + ''' Execute arbitary SQL against the database ''' + cursor = self.connection.cursor() + try: + cursor.execute(sql) + finally: + cursor.close() + + _root_passwd = '/var/lib/charm/{}/mysql.passwd' _named_passwd = '/var/lib/charm/{}/mysql-{}.passwd' @@ -111,7 +132,8 @@ def get_mysql_root_password(password=None): def configure_db(hostname, database, - username): + username, + admin=False): ''' Configure access to database for username from hostname ''' if hostname != unit_get('private-address'): remote_ip = socket.gethostbyname(hostname) @@ -126,7 +148,11 @@ def configure_db(hostname, if not m_helper.grant_exists(database, username, remote_ip): - m_helper.create_grant(database, - username, - remote_ip, password) + if not admin: + m_helper.create_grant(database, + username, + remote_ip, password) + else: + m_helper.create_admin_grant(username, + remote_ip, password) return password diff --git a/hooks/percona_hooks.py b/hooks/percona_hooks.py index bb7be94..7640b9d 100755 --- a/hooks/percona_hooks.py +++ b/hooks/percona_hooks.py @@ -15,6 +15,8 @@ from charmhelpers.core.hookenv import ( unit_get, config, service_name, + remote_unit, + relation_type ) from charmhelpers.core.host import ( service_restart, @@ -109,18 +111,53 @@ def cluster_changed(): service_restart('mysql') if eligible_leader(LEADER_RES): - files = glob.glob('/var/lib/charm/{}/*.passwd'.format(service_name())) - sync_to_peers(peer_interface='cluster', - user='juju_ssh', paths=files) + sync_files() + + +def sync_files(): + ''' Sync shared charm state files to all peers ''' + files = glob.glob('/var/lib/charm/{}/*'.format(service_name())) + sync_to_peers(peer_interface='cluster', + user='juju_ssh', paths=files) LEADER_RES = 'res_mysql_vip' +@hooks.hook('db-relation-changed') +@hooks.hook('db-admin-relation-changed') +def db_changed(): + if not eligible_leader(LEADER_RES): + log('Service is peered, clearing db relation' + ' as this service unit is not the leader') + relation_clear() + return + + if is_clustered(): + db_host = config('vip') + else: + db_host = unit_get('private-address') + + admin = relation_type() == 'db-admin-relation-changed' + database_name, _ = remote_unit().split("/") + username = database_name # TODO: is this OK? mysql used a random username + password = configure_db(relation_get('private-address'), + database_name, + username, + admin=admin) + relation_set(database=database_name, + user=username, + password=password, + host=db_host) + + sync_files() + + @hooks.hook('shared-db-relation-changed') def shared_db_changed(): if not eligible_leader(LEADER_RES): - log('MySQL service is peered, bailing shared-db relation' + log('Service is peered, clearing shared-db relation' ' as this service unit is not the leader') + relation_clear() return settings = relation_get() @@ -178,9 +215,7 @@ def shared_db_changed(): relation_set(**return_data) relation_set(db_host=db_host) - files = glob.glob('/var/lib/charm/{}/*.passwd'.format(service_name())) - sync_to_peers(peer_interface='cluster', - user='juju_ssh', paths=files) + sync_files() @hooks.hook('ha-relation-joined') @@ -220,11 +255,21 @@ def ha_relation_changed(): for r_id in relation_ids('shared-db'): relation_set(rid=r_id, db_host=config('vip')) + for r_id in relation_ids('db'): + relation_set(rid=r_id, + host=config('vip')) + for r_id in relation_ids('db-admin'): + relation_set(rid=r_id, + host=config('vip')) else: # Clear any settings data for non-leader units log('Cluster configured, not leader, clearing relation data') for r_id in relation_ids('shared-db'): relation_clear(r_id) + for r_id in relation_ids('db'): + relation_clear(r_id) + for r_id in relation_ids('db-admin'): + relation_clear(r_id) def main(): diff --git a/hooks/percona_utils.py b/hooks/percona_utils.py index c80d97d..83912be 100644 --- a/hooks/percona_utils.py +++ b/hooks/percona_utils.py @@ -126,7 +126,7 @@ def configure_mysql_root_password(password): # TODO: Submit for charmhelper -def relation_clear(r_id): +def relation_clear(r_id=None): ''' Clears any relation data already set on relation r_id ''' settings = relation_get(rid=r_id, unit=local_unit()) diff --git a/hooks/unison.py b/hooks/unison.py index 9b46798..75a7a8e 100644 --- a/hooks/unison.py +++ b/hooks/unison.py @@ -83,7 +83,7 @@ def get_keypair(user): out.write(p) subprocess.check_call(['chown', '-R', user, ssh_dir]) return open(priv_key, 'r').read().strip(), \ - open(pub_key, 'r').read().strip() + open(pub_key, 'r').read().strip() def write_authorized_keys(user, keys): @@ -127,7 +127,8 @@ def ensure_user(user, group=None): subprocess.check_call(cmd) -def ssh_authorized_peers(peer_interface, user, group=None, ensure_local_user=False): +def ssh_authorized_peers(peer_interface, user, group=None, + ensure_local_user=False): """ Main setup function, should be called from both peer -changed and -joined hooks with the same parameters.