Add file synchronization via unison/ssh.

This commit is contained in:
Adam Gandelman 2013-02-12 21:56:39 -08:00
commit b70f568191
4 changed files with 105 additions and 17 deletions

View File

@ -112,3 +112,7 @@ options:
default: "false"
type: string
description: "Enable PKI token signing (Grizzly and beyond)"
https-service-endpoints:
default: "False"
type: string
description: "Manage SSL certificates for all service endpoints."

View File

@ -3,12 +3,16 @@
import sys
import time
from base64 import b64encode
from utils import *
from lib.openstack_common import *
import lib.unison as unison
config = config_get()
packages = "keystone python-mysqldb pwgen haproxy python-jinja2"
packages = "keystone python-mysqldb pwgen haproxy python-jinja2 openssl unison"
service = "keystone"
# used to verify joined services are valid openstack components.
@ -81,6 +85,11 @@ def install_hook():
execute("service keystone stop", echo=True)
execute("keystone-manage db_sync")
execute("service keystone start", echo=True)
# ensure /var/lib/keystone is g+wrx for peer relations that
# may be syncing data there via SSH_USER.
execute("chmod -R g+wrx /var/lib/keystone/")
time.sleep(5)
ensure_initial_admin(config)
@ -97,6 +106,7 @@ def db_changed():
'private-address' not in relation_data):
juju_log("private-address or password not set. Peer not ready, exit 0")
exit(0)
update_config_block('sql', connection="mysql://%s:%s@%s/%s" %
(config["database-user"],
relation_data["password"],
@ -125,6 +135,21 @@ def db_changed():
(id, unit))
identity_changed(relation_id=id, remote_unit=unit)
def ensure_valid_service(service):
if service not in valid_services.keys():
juju_log("WARN: Invalid service requested: '%s'" % service)
realtion_set({ "admin_token": -1 })
return
def add_endpoint(region, service, public_url, admin_url, internal_url):
desc = valid_services[service]["desc"]
service_type = valid_services[service]["type"]
create_service_entry(service, service_type, desc)
create_endpoint_template(region=region, service=service,
public_url=public_url,
admin_url=admin_url,
internal_url=internal_url)
def identity_joined():
""" Do nothing until we get information about requested service """
pass
@ -170,13 +195,13 @@ def identity_changed(relation_id=None, remote_unit=None):
'internal_url'])
if single.issubset(settings):
# other end of relation advertised only one endpoint
if 'None' in [v for k,v in settings.iteritems()]:
# Some backend services advertise no endpoint but require a
# hook execution to update auth strategy.
return
ensure_valid_service(settings['service'])
add_endpoint(region=settings['region'], service=settings['service'],
publicurl=settings['public_url'],
adminurl=settings['admin_url'],
@ -250,6 +275,7 @@ def identity_changed(relation_id=None, remote_unit=None):
"service_password": service_password,
"service_tenant": config['service-tenant']
}
# Check if clustered and use vip + haproxy ports if so
if is_clustered():
relation_data["auth_host"] = config['vip']
@ -257,6 +283,17 @@ def identity_changed(relation_id=None, remote_unit=None):
relation_data["service_host"] = config['vip']
relation_data["service_port"] = SERVICE_PORTS['keystone_service']
# generate or get a new cert/key for service if set to manage certs.
if config['https-service-endpoints'] in ['True', 'true']:
ca = get_ca()
service = os.getenv('JUJU_REMOTE_UNIT').split('/')[0]
cert, key = ca.get_cert_and_key(common_name=settings['private-address'])
ca_bundle= ca.get_ca_bundle()
relation_data['ssl_cert'] = b64encode(cert)
relation_data['ssl_key'] = b64encode(key)
relation_data['ca_cert'] = b64encode(ca_bundle)
unison.sync_to_peers(peer_interface='cluster',
paths=[SSL_DIR], user=SSH_USER, verbose=True)
relation_set(relation_data)
synchronize_service_credentials()
@ -299,8 +336,17 @@ SERVICE_PORTS = {
"keystone_service": int(config['service-port']) + 1
}
def cluster_joined():
unison.ssh_authorized_peers(user=SSH_USER,
group='keystone',
peer_interface='cluster',
ensure_user=True)
def cluster_changed():
unison.ssh_authorized_peers(user=SSH_USER,
group='keystone',
peer_interface='cluster',
ensure_user=True)
cluster_hosts = {}
cluster_hosts['self'] = config['hostname']
for r_id in relation_ids('cluster'):
@ -381,6 +427,7 @@ hooks = {
"identity-service-relation-joined": identity_joined,
"identity-service-relation-changed": identity_changed,
"config-changed": config_changed,
"cluster-relation-joined": cluster_joined,
"cluster-relation-changed": cluster_changed,
"cluster-relation-departed": cluster_changed,
"ha-relation-joined": ha_relation_joined,

View File

@ -4,15 +4,25 @@ import subprocess
import sys
import json
import os
import tarfile
import tempfile
import time
from lib.openstack_common import *
import keystone_ssl as ssl
import keystone_ssh as unison
keystone_conf = "/etc/keystone/keystone.conf"
stored_passwd = "/var/lib/keystone/keystone.passwd"
stored_token = "/var/lib/keystone/keystone.token"
SERVICE_PASSWD_PATH = '/var/lib/keystone/services.passwd'
SSL_DIR = '/var/lib/keystone/juju_ssl/'
SSL_CA_NAME = 'Ubuntu Cloud'
SSH_USER='juju_keystone'
def execute(cmd, die=False, echo=False):
""" Executes a command
@ -95,6 +105,19 @@ def relation_set_2(**kwargs):
cmd += args
subprocess.check_call(cmd)
def unit_get(attribute):
cmd = [
'unit-get',
attribute
]
value = subprocess.check_output(cmd).strip() # IGNORE:E1103
if value == "":
return None
else:
return value
def relation_get(relation_data):
""" Obtain all current relation data
relation_data is a list of options to query from the relation
@ -356,8 +379,7 @@ def ensure_initial_admin(config):
create_role("KeystoneAdmin", config["admin-user"], 'admin')
create_role("KeystoneServiceAdmin", config["admin-user"], 'admin')
create_service_entry("keystone", "identity", "Keystone Identity Service")
# following documentation here, perhaps we should be using juju
# public/private addresses for public/internal urls.
if is_clustered():
juju_log("Creating endpoint for clustered configuration")
for region in config['region'].split():
@ -543,17 +565,32 @@ def synchronize_service_credentials():
Broadcast service credentials to peers or consume those that have been
broadcasted by peer, depending on hook context.
'''
if os.path.basename(sys.argv[0]) == 'cluster-relation-changed':
r_data = relation_get_dict()
if 'service_credentials' in r_data:
juju_log('Saving service passwords from peer.')
save_stored_passwords(**json.loads(r_data['service_credentials']))
return
creds = load_stored_passwords()
if not creds:
if (not eligible_leader() or
not os.path.isfile(SERVICE_PASSWD_PATH)):
return
juju_log('Synchronizing service passwords to all peers.')
creds = json.dumps(creds)
for r_id in (relation_ids('cluster') or []):
relation_set_2(rid=r_id, service_credentials=creds)
unison.sync_to_peers(peer_interface='cluster',
paths=[SERVICE_PASSWD_PATH], user=SSH_USER,
verbose=True)
CA = []
def get_ca(user='keystone', group='keystone'):
"""
Initialize a new CA object if one hasn't already been loaded.
This will create a new CA or load an existing one.
"""
if not CA:
if not os.path.isdir(SSL_DIR):
os.mkdir(SSL_DIR)
d_name = '_'.join(SSL_CA_NAME.lower().split(' '))
ca = ssl.JujuCA(name=SSL_CA_NAME,
ca_dir=os.path.join(SSL_DIR,
'%s_intermediate_ca' % d_name),
root_ca_dir=os.path.join(SSL_DIR,
'%s_root_ca' % d_name))
# SSL_DIR is synchronized via all peers over unison+ssh, need
# to ensure permissions.
execute('chown -R %s.%s %s' % (user, group, SSL_DIR))
execute('chmod -R g+rwx %s' % SSL_DIR)
CA.append(ca)
return CA[0]

View File

@ -1 +1 @@
196
197