charm-swift-proxy/hooks/swift_context.py

232 lines
7.7 KiB
Python

from charmhelpers.core.hookenv import (
config,
log,
relation_ids,
related_units,
relation_get,
unit_get
)
from charmhelpers.contrib.openstack.context import (
OSContextGenerator,
ApacheSSLContext as SSLContext,
context_complete,
CA_CERT_PATH
)
from charmhelpers.contrib.hahelpers.cluster import (
determine_api_port,
determine_apache_port,
)
from charmhelpers.contrib.openstack.utils import get_host_ip
import subprocess
import os
from charmhelpers.contrib.hahelpers.apache import (
get_cert,
get_ca_cert,
)
from base64 import b64decode, b64encode
class HAProxyContext(OSContextGenerator):
interfaces = ['cluster']
def __call__(self):
'''
Extends the main charmhelpers HAProxyContext with a port mapping
specific to this charm.
Also used to extend cinder.conf context with correct api_listening_port
'''
haproxy_port = config('bind-port')
api_port = determine_apache_port(config('bind-port'))
ctxt = {
'service_ports': {'swift_api': [haproxy_port, api_port]},
}
return ctxt
WWW_DIR = '/var/www/swift-rings'
def generate_cert():
'''
Generates a self signed certificate and key using the
provided charm configuration data.
returns: tuple of (cert, key)
'''
CERT = '/etc/swift/ssl.cert'
KEY = '/etc/swift/ssl.key'
if not os.path.exists(CERT) and not os.path.exists(KEY):
subj = '/C=%s/ST=%s/L=%s/CN=%s' %\
(config('country'), config('state'),
config('locale'), config('common-name'))
cmd = ['openssl', 'req', '-new', '-x509', '-nodes',
'-out', CERT, '-keyout', KEY,
'-subj', subj]
subprocess.check_call(cmd)
os.chmod(KEY, 0o600)
# Slurp as base64 encoded - makes handling easier up the stack
with open(CERT, 'r') as cfile:
ssl_cert = b64encode(cfile.read())
with open(KEY, 'r') as kfile:
ssl_key = b64encode(kfile.read())
return (ssl_cert, ssl_key)
class ApacheSSLContext(SSLContext):
interfaces = ['https']
external_ports = [determine_apache_port(config('bind-port'))]
service_namespace = 'swift'
def configure_cert(self):
if not os.path.isdir('/etc/apache2/ssl'):
os.mkdir('/etc/apache2/ssl')
ssl_dir = os.path.join('/etc/apache2/ssl/', self.service_namespace)
if not os.path.isdir(ssl_dir):
os.mkdir(ssl_dir)
cert, key = get_cert()
# Swift specific - generate a cert by default if not using
# a) user supplied cert or b) keystone signed cert
if None in [cert, key]:
cert, key = generate_cert()
with open(os.path.join(ssl_dir, 'cert'), 'w') as cert_out:
cert_out.write(b64decode(cert))
with open(os.path.join(ssl_dir, 'key'), 'w') as key_out:
key_out.write(b64decode(key))
ca_cert = get_ca_cert()
if ca_cert:
with open(CA_CERT_PATH, 'w') as ca_out:
ca_out.write(b64decode(ca_cert))
subprocess.check_call(['update-ca-certificates'])
def __call__(self):
return super(ApacheSSLContext, self).__call__()
class SwiftRingContext(OSContextGenerator):
def __call__(self):
allowed_hosts = []
for relid in relation_ids('swift-storage'):
for unit in related_units(relid):
host = relation_get('private-address', unit, relid)
allowed_hosts.append(get_host_ip(host))
ctxt = {
'www_dir': WWW_DIR,
'allowed_hosts': allowed_hosts
}
return ctxt
class SwiftIdentityContext(OSContextGenerator):
interfaces = ['identity-service']
def __call__(self):
bind_port = config('bind-port')
workers = config('workers')
if workers == '0':
import multiprocessing
workers = multiprocessing.cpu_count()
ctxt = {
'proxy_ip': get_host_ip(unit_get('private-address')),
'bind_port': determine_api_port(bind_port),
'workers': workers,
'operator_roles': config('operator-roles'),
'delay_auth_decision': config('delay-auth-decision'),
'node_timeout': config('node-timeout'),
'recoverable_node_timeout': config('recoverable-node-timeout'),
}
ctxt['ssl'] = False
auth_type = config('auth-type')
auth_host = config('keystone-auth-host')
admin_user = config('keystone-admin-user')
admin_password = config('keystone-admin-user')
if (auth_type == 'keystone' and auth_host
and admin_user and admin_password):
log('Using user-specified Keystone configuration.')
ks_auth = {
'auth_type': 'keystone',
'auth_protocol': config('keystone-auth-protocol'),
'keystone_host': auth_host,
'auth_port': config('keystone-auth-port'),
'service_user': admin_user,
'service_password': admin_password,
'service_tenant': config('keystone-admin-tenant-name')
}
ctxt.update(ks_auth)
for relid in relation_ids('identity-service'):
log('Using Keystone configuration from identity-service.')
for unit in related_units(relid):
ks_auth = {
'auth_type': 'keystone',
'auth_protocol': relation_get('auth_protocol',
unit, relid) or 'http',
'service_protocol': relation_get('service_protocol',
unit, relid) or 'http',
'keystone_host': relation_get('auth_host',
unit, relid),
'auth_port': relation_get('auth_port',
unit, relid),
'service_user': relation_get('service_username',
unit, relid),
'service_password': relation_get('service_password',
unit, relid),
'service_tenant': relation_get('service_tenant',
unit, relid),
'service_port': relation_get('service_port',
unit, relid),
'admin_token': relation_get('admin_token',
unit, relid),
}
if context_complete(ks_auth):
ctxt.update(ks_auth)
return ctxt
class MemcachedContext(OSContextGenerator):
def __call__(self):
ctxt = {
'proxy_ip': get_host_ip(unit_get('private-address'))
}
return ctxt
SWIFT_HASH_FILE = '/var/lib/juju/swift-hash-path.conf'
def get_swift_hash():
if os.path.isfile(SWIFT_HASH_FILE):
with open(SWIFT_HASH_FILE, 'r') as hashfile:
swift_hash = hashfile.read().strip()
elif config('swift-hash'):
swift_hash = config('swift-hash')
with open(SWIFT_HASH_FILE, 'w') as hashfile:
hashfile.write(swift_hash)
else:
cmd = ['od', '-t', 'x8', '-N', '8', '-A', 'n']
rand = open('/dev/random', 'r')
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=rand)
swift_hash = p.communicate()[0].strip()
with open(SWIFT_HASH_FILE, 'w') as hashfile:
hashfile.write(swift_hash)
return swift_hash
class SwiftHashContext(OSContextGenerator):
def __call__(self):
ctxt = {
'swift_hash': get_swift_hash()
}
return ctxt