ceph/teuthology/task/ssh_keys.py
Warren Usui 6df622b353 Fix docstrings and minor formatting in task/ssh_keys.py
Fixes: 6539
Signed-off-by: Warren Usui <warren.usui@inktank.com>
2014-02-14 15:48:38 -08:00

181 lines
5.8 KiB
Python

#!/usr/bin/python
"""
Ssh-key key handlers and associated routines
"""
import contextlib
import logging
import paramiko
import re
from cStringIO import StringIO
from teuthology import contextutil
import teuthology.misc as misc
from ..orchestra import run
log = logging.getLogger(__name__)
ssh_keys_user = 'ssh-keys-user'
def generate_keys():
"""
Generatees a public and private key
"""
key = paramiko.RSAKey.generate(2048)
privateString = StringIO()
key.write_private_key(privateString)
return key.get_base64(), privateString.getvalue()
def particular_ssh_key_test(line_to_test, ssh_key):
"""
Check the validity of the ssh_key
"""
match = re.match('[\w-]+ {key} \S+@\S+'.format(key=re.escape(ssh_key)), line_to_test)
if match:
return False
else:
return True
def ssh_keys_user_line_test(line_to_test, username ):
"""
Check the validity of the username
"""
match = re.match('[\w-]+ \S+ {username}@\S+'.format(username=username), line_to_test)
if match:
return False
else:
return True
def cleanup_added_key(ctx):
"""
Delete the keys and removes ~/.ssh/authorized_keys2 entries we added
"""
log.info('cleaning up keys added for testing')
for remote in ctx.cluster.remotes:
username, hostname = str(remote).split('@')
if "" == username or "" == hostname:
continue
else:
log.info(' cleaning up keys for user {user} on {host}'.format(host=hostname, user=username))
misc.delete_file(remote, '/home/{user}/.ssh/id_rsa'.format(user=username))
misc.delete_file(remote, '/home/{user}/.ssh/id_rsa.pub'.format(user=username))
misc.delete_file(remote, '/home/{user}/.ssh/authorized_keys2'.format(user=username))
@contextlib.contextmanager
def tweak_ssh_config(ctx, config):
"""
Turn off StrictHostKeyChecking
"""
run.wait(
ctx.cluster.run(
args=[
'echo',
'StrictHostKeyChecking no\n',
run.Raw('>'),
run.Raw('/home/ubuntu/.ssh/config'),
run.Raw('&&'),
'echo',
'UserKnownHostsFile ',
run.Raw('/dev/null'),
run.Raw('>>'),
run.Raw('/home/ubuntu/.ssh/config'),
run.Raw('&&'),
run.Raw('chmod 600 /home/ubuntu/.ssh/config'),
],
wait=False,
)
)
try:
yield
finally:
run.wait(
ctx.cluster.run(
args=['rm',run.Raw('/home/ubuntu/.ssh/config')],
wait=False
),
)
@contextlib.contextmanager
def push_keys_to_host(ctx, config, public_key, private_key):
"""
Push keys to all hosts
"""
log.info('generated public key {pub_key}'.format(pub_key=public_key))
# add an entry for all hosts in ctx to auth_keys_data
auth_keys_data = ''
for inner_host in ctx.cluster.remotes.iterkeys():
inner_username, inner_hostname = str(inner_host).split('@')
# create a 'user@hostname' string using our fake hostname
fake_hostname = '{user}@{host}'.format(user=ssh_keys_user, host=str(inner_hostname))
auth_keys_data += '\nssh-rsa {pub_key} {user_host}\n'.format(pub_key=public_key, user_host=fake_hostname)
# for each host in ctx, add keys for all other hosts
for remote in ctx.cluster.remotes:
username, hostname = str(remote).split('@')
if "" == username or "" == hostname:
continue
else:
log.info('pushing keys to {host} for {user}'.format(host=hostname, user=username))
# adding a private key
priv_key_file = '/home/{user}/.ssh/id_rsa'.format(user=username)
priv_key_data = '{priv_key}'.format(priv_key=private_key)
misc.delete_file(remote, priv_key_file, force=True)
# Hadoop requires that .ssh/id_rsa have permissions of '500'
misc.create_file(remote, priv_key_file, priv_key_data, str(500))
# then a private key
pub_key_file = '/home/{user}/.ssh/id_rsa.pub'.format(user=username)
pub_key_data = 'ssh-rsa {pub_key} {user_host}'.format(pub_key=public_key, user_host=str(remote))
misc.delete_file(remote, pub_key_file, force=True)
misc.create_file(remote, pub_key_file, pub_key_data)
# adding appropriate entries to the authorized_keys2 file for this host
auth_keys_file = '/home/{user}/.ssh/authorized_keys2'.format(user=username)
# now add the list of keys for hosts in ctx to ~/.ssh/authorized_keys2
misc.create_file(remote, auth_keys_file, auth_keys_data, str(600))
try:
yield
finally:
# cleanup the keys
log.info("Cleaning up SSH keys")
cleanup_added_key(ctx)
@contextlib.contextmanager
def task(ctx, config):
"""
Creates a set of RSA keys, distributes the same key pair
to all hosts listed in ctx.cluster, and adds all hosts
to all others authorized_keys list.
During cleanup it will delete .ssh/id_rsa, .ssh/id_rsa.pub
and remove the entries in .ssh/authorized_keys while leaving
pre-existing entries in place.
"""
if config is None:
config = {}
assert isinstance(config, dict), \
"task hadoop only supports a dictionary for configuration"
# this does not need to do cleanup and does not depend on
# ctx, so I'm keeping it outside of the nested calls
public_key_string, private_key_string = generate_keys()
with contextutil.nested(
lambda: tweak_ssh_config(ctx, config),
lambda: push_keys_to_host(ctx, config, public_key_string, private_key_string),
#lambda: tweak_ssh_config(ctx, config),
):
yield