Add missing hooks and files, tidy lint

This commit is contained in:
James Page
2013-09-19 16:40:27 +01:00
parent 30b31dc3f3
commit 412cedd860
6 changed files with 250 additions and 9 deletions

View File

@@ -0,0 +1 @@
percona_hooks.py

View File

@@ -15,7 +15,7 @@ from charmhelpers.core.hookenv import (
relation_ids,
unit_get,
config,
service_name
service_name,
)
from charmhelpers.core.host import (
service_restart,
@@ -34,7 +34,8 @@ from percona_utils import (
get_cluster_hosts,
configure_sstuser,
seeded, mark_seeded,
configure_mysql_root_password
configure_mysql_root_password,
relation_clear,
)
from mysql import get_mysql_password
from charmhelpers.contrib.hahelpers.cluster import (
@@ -67,15 +68,15 @@ def render_config(clustered=False, hosts=[]):
if not os.path.exists(os.path.dirname(MY_CNF)):
os.makedirs(os.path.dirname(MY_CNF))
with open(MY_CNF, 'w') as conf:
conf.write(render_template(os.path.basename(MY_CNF),
{'cluster_name': 'juju_cluster',
context = {
'cluster_name': 'juju_cluster',
'private_address': get_host_ip(),
'clustered': clustered,
'cluster_hosts': ",".join(hosts),
'sst_password': get_mysql_password(username='sstuser',
password=config('sst-password'))
})
)
}
conf.write(render_template(os.path.basename(MY_CNF), context))
# TODO: set 0640 and change group to mysql if avaliable
os.chmod(MY_CNF, 0644)
@@ -197,7 +198,7 @@ def ha_relation_joined():
resources = {'res_mysql_vip': 'ocf:heartbeat:IPaddr2'}
resource_params = {
'res_mysql_vip': 'params ip="%s" cidr_netmask="%s" nic="%s"' % \
'res_mysql_vip': 'params ip="%s" cidr_netmask="%s" nic="%s"' %
(vip, vip_cidr, vip_iface),
}
groups = {'grp_percona_cluster': 'res_mysql_vip'}
@@ -221,8 +222,10 @@ def ha_relation_changed():
relation_set(rid=r_id,
db_host=config('vip'))
else:
# TODO: Unset any data already set if not leader
pass
# 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)
def main():

View File

@@ -11,6 +11,8 @@ from charmhelpers.core.hookenv import (
relation_ids,
related_units,
relation_get,
relation_set,
local_unit,
)
from charmhelpers.fetch import (
apt_install,
@@ -121,3 +123,15 @@ def configure_mysql_root_password(password):
(package, package, root_pass))
dconf.communicate()
dconf.wait()
# TODO: Submit for charmhelper
def relation_clear(r_id):
''' Clears any relation data already set on relation r_id '''
settings = relation_get(rid=r_id,
unit=local_unit())
for setting in settings:
if setting not in ['public-address', 'private-address']:
settings[setting] = None
relation_set(relation_id=r_id,
**settings)

1
hooks/start Symbolic link
View File

@@ -0,0 +1 @@
percona_hooks.py

1
hooks/stop Symbolic link
View File

@@ -0,0 +1 @@
percona_hooks.py

221
hooks/unison.py Normal file
View File

@@ -0,0 +1,221 @@
#!/usr/bin/python
#
# Easy file synchronization among peer units using ssh + unison.
#
# From *both* peer relation -joined and -changed, add a call to
# ssh_authorized_peers() describing the peer relation and the desired
# user + group. After all peer relations have settled, all hosts should
# be able to connect to on another via key auth'd ssh as the specified user.
#
# Other hooks are then free to synchronize files and directories using
# sync_to_peers().
#
# For a peer relation named 'cluster', for example:
#
# cluster-relation-joined:
# ...
# ssh_authorized_peers(peer_interface='cluster',
# user='juju_ssh', group='juju_ssh',
# ensure_user=True)
# ...
#
# cluster-relation-changed:
# ...
# ssh_authorized_peers(peer_interface='cluster',
# user='juju_ssh', group='juju_ssh',
# ensure_user=True)
# ...
#
# Hooks are now free to sync files as easily as:
#
# files = ['/etc/fstab', '/etc/apt.conf.d/']
# sync_to_peers(peer_interface='cluster',
# user='juju_ssh, paths=[files])
#
# It is assumed the charm itself has setup permissions on each unit
# such that 'juju_ssh' has read + write permissions. Also assumed
# that the calling charm takes care of leader delegation.
import os
import sys
import subprocess
import grp
import pwd
from charmhelpers.core.hookenv import (
log,
relation_ids,
related_units,
relation_get,
relation_set,
unit_get
)
def get_homedir(user):
try:
user = pwd.getpwnam(user)
return user.pw_dir
except KeyError:
log('Could not get homedir for user %s: user exists?')
sys.exit(1)
def get_keypair(user):
home_dir = get_homedir(user)
ssh_dir = os.path.join(home_dir, '.ssh')
if not os.path.isdir(ssh_dir):
os.mkdir(ssh_dir)
priv_key = os.path.join(ssh_dir, 'id_rsa')
if not os.path.isfile(priv_key):
log('Generating new ssh key for user %s.' % user)
cmd = ['ssh-keygen', '-q', '-N', '', '-t', 'rsa', '-b', '2048',
'-f', priv_key]
subprocess.check_call(cmd)
pub_key = '%s.pub' % priv_key
if not os.path.isfile(pub_key):
log('Generatring missing ssh public key @ %s.' %
pub_key)
cmd = ['ssh-keygen', '-y', '-f', priv_key]
p = subprocess.check_output(cmd).strip()
with open(pub_key, 'wb') as out:
out.write(p)
subprocess.check_call(['chown', '-R', user, ssh_dir])
return open(priv_key, 'r').read().strip(), \
open(pub_key, 'r').read().strip()
def write_authorized_keys(user, keys):
home_dir = get_homedir(user)
ssh_dir = os.path.join(home_dir, '.ssh')
auth_keys = os.path.join(ssh_dir, 'authorized_keys')
log('Syncing authorized_keys @ %s.' % auth_keys)
with open(auth_keys, 'wb') as out:
for k in keys:
out.write('%s\n' % k)
def write_known_hosts(user, hosts):
home_dir = get_homedir(user)
ssh_dir = os.path.join(home_dir, '.ssh')
known_hosts = os.path.join(ssh_dir, 'known_hosts')
khosts = []
for host in hosts:
cmd = ['ssh-keyscan', '-H', '-t', 'rsa', host]
remote_key = subprocess.check_output(cmd).strip()
khosts.append(remote_key)
log('Syncing known_hosts @ %s.' % known_hosts)
with open(known_hosts, 'wb') as out:
for host in khosts:
out.write('%s\n' % host)
def ensure_user(user, group=None):
# need to ensure a bash shell'd user exists.
try:
pwd.getpwnam(user)
except KeyError:
log('Creating new user %s.%s.' % (user, group))
cmd = ['adduser', '--system', '--shell', '/bin/bash', user]
if group:
try:
grp.getgrnam(group)
except KeyError:
subprocess.check_call(['addgroup', group])
cmd += ['--ingroup', group]
subprocess.check_call(cmd)
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.
"""
if ensure_local_user:
ensure_user(user, group)
priv_key, pub_key = get_keypair(user)
hook = os.path.basename(sys.argv[0])
if hook == '%s-relation-joined' % peer_interface:
relation_set(ssh_pub_key=pub_key)
print 'joined'
elif hook == '%s-relation-changed' % peer_interface:
hosts = []
keys = []
for r_id in relation_ids(peer_interface):
for unit in related_units(r_id):
settings = relation_get(rid=r_id,
unit=unit)
if 'ssh_pub_key' in settings:
keys.append(settings['ssh_pub_key'])
hosts.append(settings['private-address'])
else:
log('ssh_authorized_peers(): ssh_pub_key '
'missing for unit %s, skipping.' % unit)
write_authorized_keys(user, keys)
write_known_hosts(user, hosts)
authed_hosts = ':'.join(hosts)
relation_set(ssh_authorized_hosts=authed_hosts)
def _run_as_user(user):
try:
user = pwd.getpwnam(user)
except KeyError:
log('Invalid user: %s' % user)
sys.exit(1)
uid, gid = user.pw_uid, user.pw_gid
os.environ['HOME'] = user.pw_dir
def _inner():
os.setgid(gid)
os.setuid(uid)
return _inner
def run_as_user(user, cmd):
return subprocess.check_output(cmd, preexec_fn=_run_as_user(user), cwd='/')
def sync_to_peers(peer_interface, user, paths=[], verbose=False):
base_cmd = ['unison', '-auto', '-batch=true', '-confirmbigdel=false',
'-fastcheck=true', '-group=false', '-owner=false',
'-prefer=newer']
if not verbose:
base_cmd.append('-silent')
hosts = []
for r_id in (relation_ids(peer_interface) or []):
for unit in related_units(r_id):
settings = relation_get(rid=r_id,
unit=unit)
try:
authed_hosts = settings['ssh_authorized_hosts'].split(':')
except KeyError:
log('unison sync_to_peers: peer has not authorized *any* '
'hosts yet.')
return
unit_hostname = unit_get('private-address')
add_host = None
for authed_host in authed_hosts:
if unit_hostname == authed_host:
add_host = settings['private-address']
if add_host:
hosts.append(settings['private-address'])
else:
log('unison sync_to_peers: peer (%s) has not authorized '
'*this* host yet, skipping.' %
settings['private-address'])
for path in paths:
# removing trailing slash from directory paths, unison
# doesn't like these.
if path.endswith('/'):
path = path[:(len(path) - 1)]
for host in hosts:
cmd = base_cmd + [path, 'ssh://%s@%s/%s' % (user, host, path)]
log('Syncing local path %s to %s@%s:%s' %
(path, user, host, path))
log(' '.join(cmd))
run_as_user(user, cmd)