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" default: "false"
type: string type: string
description: "Enable PKI token signing (Grizzly and beyond)" 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 sys
import time import time
from base64 import b64encode
from utils import * from utils import *
from lib.openstack_common import * from lib.openstack_common import *
import lib.unison as unison
config = config_get() config = config_get()
packages = "keystone python-mysqldb pwgen haproxy python-jinja2" packages = "keystone python-mysqldb pwgen haproxy python-jinja2 openssl unison"
service = "keystone" service = "keystone"
# used to verify joined services are valid openstack components. # used to verify joined services are valid openstack components.
@ -81,6 +85,11 @@ def install_hook():
execute("service keystone stop", echo=True) execute("service keystone stop", echo=True)
execute("keystone-manage db_sync") execute("keystone-manage db_sync")
execute("service keystone start", echo=True) 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) time.sleep(5)
ensure_initial_admin(config) ensure_initial_admin(config)
@ -97,6 +106,7 @@ def db_changed():
'private-address' not in relation_data): 'private-address' not in relation_data):
juju_log("private-address or password not set. Peer not ready, exit 0") juju_log("private-address or password not set. Peer not ready, exit 0")
exit(0) exit(0)
update_config_block('sql', connection="mysql://%s:%s@%s/%s" % update_config_block('sql', connection="mysql://%s:%s@%s/%s" %
(config["database-user"], (config["database-user"],
relation_data["password"], relation_data["password"],
@ -125,6 +135,21 @@ def db_changed():
(id, unit)) (id, unit))
identity_changed(relation_id=id, remote_unit=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(): def identity_joined():
""" Do nothing until we get information about requested service """ """ Do nothing until we get information about requested service """
pass pass
@ -170,13 +195,13 @@ def identity_changed(relation_id=None, remote_unit=None):
'internal_url']) 'internal_url'])
if single.issubset(settings): if single.issubset(settings):
# other end of relation advertised only one endpoint # other end of relation advertised only one endpoint
if 'None' in [v for k,v in settings.iteritems()]: if 'None' in [v for k,v in settings.iteritems()]:
# Some backend services advertise no endpoint but require a # Some backend services advertise no endpoint but require a
# hook execution to update auth strategy. # hook execution to update auth strategy.
return return
ensure_valid_service(settings['service']) ensure_valid_service(settings['service'])
add_endpoint(region=settings['region'], service=settings['service'], add_endpoint(region=settings['region'], service=settings['service'],
publicurl=settings['public_url'], publicurl=settings['public_url'],
adminurl=settings['admin_url'], adminurl=settings['admin_url'],
@ -250,6 +275,7 @@ def identity_changed(relation_id=None, remote_unit=None):
"service_password": service_password, "service_password": service_password,
"service_tenant": config['service-tenant'] "service_tenant": config['service-tenant']
} }
# Check if clustered and use vip + haproxy ports if so # Check if clustered and use vip + haproxy ports if so
if is_clustered(): if is_clustered():
relation_data["auth_host"] = config['vip'] 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_host"] = config['vip']
relation_data["service_port"] = SERVICE_PORTS['keystone_service'] 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) relation_set(relation_data)
synchronize_service_credentials() synchronize_service_credentials()
@ -299,8 +336,17 @@ SERVICE_PORTS = {
"keystone_service": int(config['service-port']) + 1 "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(): def cluster_changed():
unison.ssh_authorized_peers(user=SSH_USER,
group='keystone',
peer_interface='cluster',
ensure_user=True)
cluster_hosts = {} cluster_hosts = {}
cluster_hosts['self'] = config['hostname'] cluster_hosts['self'] = config['hostname']
for r_id in relation_ids('cluster'): for r_id in relation_ids('cluster'):
@ -381,6 +427,7 @@ hooks = {
"identity-service-relation-joined": identity_joined, "identity-service-relation-joined": identity_joined,
"identity-service-relation-changed": identity_changed, "identity-service-relation-changed": identity_changed,
"config-changed": config_changed, "config-changed": config_changed,
"cluster-relation-joined": cluster_joined,
"cluster-relation-changed": cluster_changed, "cluster-relation-changed": cluster_changed,
"cluster-relation-departed": cluster_changed, "cluster-relation-departed": cluster_changed,
"ha-relation-joined": ha_relation_joined, "ha-relation-joined": ha_relation_joined,

View File

@ -4,15 +4,25 @@ import subprocess
import sys import sys
import json import json
import os import os
import tarfile
import tempfile
import time import time
from lib.openstack_common import * from lib.openstack_common import *
import keystone_ssl as ssl
import keystone_ssh as unison
keystone_conf = "/etc/keystone/keystone.conf" keystone_conf = "/etc/keystone/keystone.conf"
stored_passwd = "/var/lib/keystone/keystone.passwd" stored_passwd = "/var/lib/keystone/keystone.passwd"
stored_token = "/var/lib/keystone/keystone.token" stored_token = "/var/lib/keystone/keystone.token"
SERVICE_PASSWD_PATH = '/var/lib/keystone/services.passwd' 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): def execute(cmd, die=False, echo=False):
""" Executes a command """ Executes a command
@ -95,6 +105,19 @@ def relation_set_2(**kwargs):
cmd += args cmd += args
subprocess.check_call(cmd) 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): def relation_get(relation_data):
""" Obtain all current relation data """ Obtain all current relation data
relation_data is a list of options to query from the relation 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("KeystoneAdmin", config["admin-user"], 'admin')
create_role("KeystoneServiceAdmin", config["admin-user"], 'admin') create_role("KeystoneServiceAdmin", config["admin-user"], 'admin')
create_service_entry("keystone", "identity", "Keystone Identity Service") 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(): if is_clustered():
juju_log("Creating endpoint for clustered configuration") juju_log("Creating endpoint for clustered configuration")
for region in config['region'].split(): 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 Broadcast service credentials to peers or consume those that have been
broadcasted by peer, depending on hook context. broadcasted by peer, depending on hook context.
''' '''
if os.path.basename(sys.argv[0]) == 'cluster-relation-changed': if (not eligible_leader() or
r_data = relation_get_dict() not os.path.isfile(SERVICE_PASSWD_PATH)):
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:
return return
juju_log('Synchronizing service passwords to all peers.') juju_log('Synchronizing service passwords to all peers.')
creds = json.dumps(creds) unison.sync_to_peers(peer_interface='cluster',
for r_id in (relation_ids('cluster') or []): paths=[SERVICE_PASSWD_PATH], user=SSH_USER,
relation_set_2(rid=r_id, service_credentials=creds) 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