This commit is contained in:
yolanda.robla@canonical.com
2014-03-28 12:52:48 +01:00
12 changed files with 151 additions and 63 deletions

View File

@@ -1,4 +1,4 @@
branch: lp:~openstack-charmers/charm-helpers/icehouse
branch: lp:charm-helpers
destination: hooks/charmhelpers
include:
- core

View File

@@ -121,6 +121,11 @@ options:
ssl_key:
type: string
description: SSL key to use with certificate specified as ssl_cert.
ssl_ca:
type: string
description: |
SSL CA to use with the certificate and key provided - this is only
required if you are providing a privately signed ssl_cert and ssl_key.
config-flags:
type: string
description: Comma separated list of key=value config flags to be set in cinder.conf.

View File

@@ -39,14 +39,15 @@ def get_cert():
def get_ca_cert():
ca_cert = None
log("Inspecting identity-service relations for CA SSL certificate.",
level=INFO)
for r_id in relation_ids('identity-service'):
for unit in relation_list(r_id):
if not ca_cert:
ca_cert = relation_get('ca_cert',
rid=r_id, unit=unit)
ca_cert = config_get('ssl_ca')
if ca_cert is None:
log("Inspecting identity-service relations for CA SSL certificate.",
level=INFO)
for r_id in relation_ids('identity-service'):
for unit in relation_list(r_id):
if ca_cert is None:
ca_cert = relation_get('ca_cert',
rid=r_id, unit=unit)
return ca_cert

View File

@@ -1,5 +1,6 @@
import json
import os
import time
from base64 import b64decode
@@ -113,7 +114,8 @@ class OSContextGenerator(object):
class SharedDBContext(OSContextGenerator):
interfaces = ['shared-db']
def __init__(self, database=None, user=None, relation_prefix=None):
def __init__(self,
database=None, user=None, relation_prefix=None, ssl_dir=None):
'''
Allows inspecting relation for settings prefixed with relation_prefix.
This is useful for parsing access for multiple databases returned via
@@ -122,6 +124,7 @@ class SharedDBContext(OSContextGenerator):
self.relation_prefix = relation_prefix
self.database = database
self.user = user
self.ssl_dir = ssl_dir
def __call__(self):
self.database = self.database or config('database')
@@ -139,10 +142,9 @@ class SharedDBContext(OSContextGenerator):
for rid in relation_ids('shared-db'):
for unit in related_units(rid):
passwd = relation_get(password_setting, rid=rid, unit=unit)
rdata = relation_get(rid=rid, unit=unit)
ctxt = {
'database_host': relation_get('db_host', rid=rid,
unit=unit),
'database_host': rdata.get('db_host'),
'database': self.database,
'database_user': self.user,
'database_password': passwd,
@@ -178,10 +180,36 @@ class PostgresqlDBContext(OSContextGenerator):
'database_type': 'postgresql',
}
if context_complete(ctxt):
db_ssl(rdata, ctxt, self.ssl_dir)
return ctxt
return {}
def db_ssl(rdata, ctxt, ssl_dir):
if 'ssl_ca' in rdata and ssl_dir:
ca_path = os.path.join(ssl_dir, 'db-client.ca')
with open(ca_path, 'w') as fh:
fh.write(b64decode(rdata['ssl_ca']))
ctxt['database_ssl_ca'] = ca_path
elif 'ssl_ca' in rdata:
log("Charm not setup for ssl support but ssl ca found")
return ctxt
if 'ssl_cert' in rdata:
cert_path = os.path.join(
ssl_dir, 'db-client.cert')
if not os.path.exists(cert_path):
log("Waiting 1m for ssl client cert validity")
time.sleep(60)
with open(cert_path, 'w') as fh:
fh.write(b64decode(rdata['ssl_cert']))
ctxt['database_ssl_cert'] = cert_path
key_path = os.path.join(ssl_dir, 'db-client.key')
with open(key_path, 'w') as fh:
fh.write(b64decode(rdata['ssl_key']))
ctxt['database_ssl_key'] = key_path
return ctxt
class IdentityServiceContext(OSContextGenerator):
interfaces = ['identity-service']
@@ -191,22 +219,19 @@ class IdentityServiceContext(OSContextGenerator):
for rid in relation_ids('identity-service'):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
ctxt = {
'service_port': relation_get('service_port', rid=rid,
unit=unit),
'service_host': relation_get('service_host', rid=rid,
unit=unit),
'auth_host': relation_get('auth_host', rid=rid, unit=unit),
'auth_port': relation_get('auth_port', rid=rid, unit=unit),
'admin_tenant_name': relation_get('service_tenant',
rid=rid, unit=unit),
'admin_user': relation_get('service_username', rid=rid,
unit=unit),
'admin_password': relation_get('service_password', rid=rid,
unit=unit),
# XXX: Hard-coded http.
'service_protocol': 'http',
'auth_protocol': 'http',
'service_port': rdata.get('service_port'),
'service_host': rdata.get('service_host'),
'auth_host': rdata.get('auth_host'),
'auth_port': rdata.get('auth_port'),
'admin_tenant_name': rdata.get('service_tenant'),
'admin_user': rdata.get('service_username'),
'admin_password': rdata.get('service_password'),
'service_protocol':
rdata.get('service_protocol') or 'http',
'auth_protocol':
rdata.get('auth_protocol') or 'http',
}
if context_complete(ctxt):
return ctxt
@@ -216,6 +241,9 @@ class IdentityServiceContext(OSContextGenerator):
class AMQPContext(OSContextGenerator):
interfaces = ['amqp']
def __init__(self, ssl_dir=None):
self.ssl_dir = ssl_dir
def __call__(self):
log('Generating template context for amqp')
conf = config()
@@ -226,9 +254,9 @@ class AMQPContext(OSContextGenerator):
log('Could not generate shared_db context. '
'Missing required charm config options: %s.' % e)
raise OSContextError
ctxt = {}
for rid in relation_ids('amqp'):
ha_vip_only = False
for unit in related_units(rid):
if relation_get('clustered', rid=rid, unit=unit):
ctxt['clustered'] = True
@@ -243,16 +271,36 @@ class AMQPContext(OSContextGenerator):
unit=unit),
'rabbitmq_virtual_host': vhost,
})
ssl_port = relation_get('ssl_port', rid=rid, unit=unit)
if ssl_port:
ctxt['rabbit_ssl_port'] = ssl_port
ssl_ca = relation_get('ssl_ca', rid=rid, unit=unit)
if ssl_ca:
ctxt['rabbit_ssl_ca'] = ssl_ca
if relation_get('ha_queues', rid=rid, unit=unit) is not None:
ctxt['rabbitmq_ha_queues'] = True
ha_vip_only = relation_get('ha-vip-only',
rid=rid, unit=unit) is not None
if context_complete(ctxt):
if 'rabbit_ssl_ca' in ctxt:
if not self.ssl_dir:
log(("Charm not setup for ssl support "
"but ssl ca found"))
break
ca_path = os.path.join(
self.ssl_dir, 'rabbit-client-ca.pem')
with open(ca_path, 'w') as fh:
fh.write(b64decode(ctxt['rabbit_ssl_ca']))
ctxt['rabbit_ssl_ca'] = ca_path
# Sufficient information found = break out!
break
# Used for active/active rabbitmq >= grizzly
if ('clustered' not in ctxt or relation_get('ha-vip-only') == 'True') and \
len(related_units(rid)) > 1:
if relation_get('ha_queues'):
ctxt['rabbitmq_ha_queues'] = relation_get('ha_queues')
else:
ctxt['rabbitmq_ha_queues'] = False
if ('clustered' not in ctxt or ha_vip_only) \
and len(related_units(rid)) > 1:
rabbitmq_hosts = []
for unit in related_units(rid):
rabbitmq_hosts.append(relation_get('private-address',
@@ -418,6 +466,8 @@ class ApacheSSLContext(OSContextGenerator):
'private_address': unit_get('private-address'),
'endpoints': []
}
if is_clustered():
ctxt['private_address'] = config('vip')
for api_port in self.external_ports:
ext_port = determine_apache_port(api_port)
int_port = determine_api_port(api_port)

View File

@@ -17,6 +17,8 @@ def headers_package():
kver = check_output(['uname', '-r']).strip()
return 'linux-headers-%s' % kver
QUANTUM_CONF_DIR = '/etc/quantum'
def kernel_version():
""" Retrieve the current major kernel version as a tuple e.g. (3, 13) """
@@ -35,6 +37,8 @@ def determine_dkms_package():
# legacy
def quantum_plugins():
from charmhelpers.contrib.openstack import context
return {
@@ -46,7 +50,8 @@ def quantum_plugins():
'contexts': [
context.SharedDBContext(user=config('neutron-database-user'),
database=config('neutron-database'),
relation_prefix='neutron')],
relation_prefix='neutron',
ssl_dir=QUANTUM_CONF_DIR)],
'services': ['quantum-plugin-openvswitch-agent'],
'packages': [[headers_package()] + determine_dkms_package(),
['quantum-plugin-openvswitch-agent']],
@@ -61,7 +66,8 @@ def quantum_plugins():
'contexts': [
context.SharedDBContext(user=config('neutron-database-user'),
database=config('neutron-database'),
relation_prefix='neutron')],
relation_prefix='neutron',
ssl_dir=QUANTUM_CONF_DIR)],
'services': [],
'packages': [],
'server_packages': ['quantum-server',
@@ -70,6 +76,8 @@ def quantum_plugins():
}
}
NEUTRON_CONF_DIR = '/etc/neutron'
def neutron_plugins():
from charmhelpers.contrib.openstack import context
@@ -83,7 +91,8 @@ def neutron_plugins():
'contexts': [
context.SharedDBContext(user=config('neutron-database-user'),
database=config('neutron-database'),
relation_prefix='neutron')],
relation_prefix='neutron',
ssl_dir=NEUTRON_CONF_DIR)],
'services': ['neutron-plugin-openvswitch-agent'],
'packages': [[headers_package()] + determine_dkms_package(),
['neutron-plugin-openvswitch-agent']],
@@ -98,7 +107,8 @@ def neutron_plugins():
'contexts': [
context.SharedDBContext(user=config('neutron-database-user'),
database=config('neutron-database'),
relation_prefix='neutron')],
relation_prefix='neutron',
ssl_dir=NEUTRON_CONF_DIR)],
'services': [],
'packages': [],
'server_packages': ['neutron-server',

View File

@@ -3,12 +3,13 @@
# cinder configuration file maintained by Juju
# local changes may be overwritten.
###############################################################################
{% if auth -%}
[global]
{% if auth -%}
auth_supported = {{ auth }}
keyring = /etc/ceph/$cluster.$name.keyring
mon host = {{ mon_hosts }}
{% endif -%}
log to syslog = {{ use_syslog }}
err to syslog = {{ use_syslog }}
clog to syslog = {{ use_syslog }}
log to syslog = {{ use_syslog }}
err to syslog = {{ use_syslog }}
clog to syslog = {{ use_syslog }}

View File

@@ -423,19 +423,19 @@ def get_hostname(address, fqdn=True):
Resolves hostname for given IP, or returns the input
if it is already a hostname.
"""
if not is_ip(address):
return address
if is_ip(address):
try:
import dns.reversename
except ImportError:
apt_install('python-dnspython')
import dns.reversename
try:
import dns.reversename
except ImportError:
apt_install('python-dnspython')
import dns.reversename
rev = dns.reversename.from_address(address)
result = ns_query(rev)
if not result:
return None
rev = dns.reversename.from_address(address)
result = ns_query(rev)
if not result:
return None
else:
result = address
if fqdn:
# strip trailing .

View File

@@ -90,8 +90,9 @@ CLUSTER_RES = 'res_cinder_vip'
class CinderCharmError(Exception):
pass
CINDER_CONF = '/etc/cinder/cinder.conf'
CINDER_API_CONF = '/etc/cinder/api-paste.ini'
CINDER_CONF_DIR = "/etc/cinder"
CINDER_CONF = '%s/cinder.conf' % CINDER_CONF_DIR
CINDER_API_CONF = '%s/api-paste.ini' % CINDER_CONF_DIR
CEPH_CONF = '/etc/ceph/ceph.conf'
CHARM_CEPH_CONF = '/var/lib/charm/{}/ceph.conf'
@@ -109,9 +110,9 @@ def ceph_config_file():
# with file in restart_on_changes()'s service map.
CONFIG_FILES = OrderedDict([
(CINDER_CONF, {
'hook_contexts': [context.SharedDBContext(),
'hook_contexts': [context.SharedDBContext(ssl_dir=CINDER_CONF_DIR),
context.PostgresqlDBContext(),
context.AMQPContext(),
context.AMQPContext(ssl_dir=CINDER_CONF_DIR),
context.ImageServiceContext(),
context.OSConfigFlagContext(),
context.SyslogContext(),

View File

@@ -16,11 +16,18 @@ state_path = /var/lib/cinder
lock_path = /var/lock/cinder
volumes_dir = /var/lib/cinder/volumes
{% if database_host -%}
sql_connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}
sql_connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %}
{% endif -%}
{% if rabbitmq_host -%}
{% if rabbitmq_host %}
notification_driver = cinder.openstack.common.notifier.rabbit_notifier
control_exchange = cinder
{% if rabbit_ssl_port %}
rabbit_use_ssl=True
rabbit_port={{ rabbit_ssl_port }}
{% if rabbit_ssl_ca %}
kombu_ssl_ca_certs={{rabbit_ssl_ca}}
{% endif %}
{% endif %}
rabbit_host = {{ rabbitmq_host }}
rabbit_userid = {{ rabbitmq_user }}
rabbit_password = {{ rabbitmq_password }}

View File

@@ -58,6 +58,9 @@ paste.filter_factory = cinder.api.middleware.auth:CinderKeystoneContext.factory
[filter:authtoken]
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
{% if service_host -%}
service_protocol = {{ service_protocol }}
service_host = {{ service_host }}
service_port = {{ service_port }}
auth_host = {{ auth_host }}
auth_port = {{ auth_port }}
auth_protocol = {{ auth_protocol }}

View File

@@ -19,6 +19,13 @@ volumes_dir = /var/lib/cinder/volumes
{% if rabbitmq_host or rabbitmq_hosts -%}
notification_driver = cinder.openstack.common.notifier.rpc_notifier
control_exchange = cinder
{% if rabbit_ssl_port %}
rabbit_use_ssl=True
rabbit_port={{ rabbit_ssl_port }}
{% if rabbit_ssl_ca %}
kombu_ssl_ca_certs={{rabbit_ssl_ca}}
{% endif %}
{% endif %}
rabbit_userid = {{ rabbitmq_user }}
rabbit_password = {{ rabbitmq_password }}
rabbit_virtual_host = {{ rabbitmq_virtual_host }}
@@ -62,5 +69,5 @@ glance_api_version = {{ glance_api_version }}
{% if database_host -%}
[database]
connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}
connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %}
{% endif -%}

View File

@@ -73,6 +73,7 @@ class TestCinderContext(CharmTestCase):
service_enabled.return_value = False
self.assertEquals(contexts.ApacheSSLContext()(), {})
@patch('charmhelpers.contrib.openstack.context.is_clustered')
@patch('charmhelpers.contrib.openstack.context.determine_apache_port')
@patch('charmhelpers.contrib.openstack.context.determine_api_port')
@patch('charmhelpers.contrib.openstack.context.unit_get')
@@ -81,11 +82,13 @@ class TestCinderContext(CharmTestCase):
def test_apache_ssl_context_service_enabled(self, service_enabled,
mock_https, mock_unit_get,
mock_determine_api_port,
mock_determine_apache_port):
mock_determine_apache_port,
mock_is_clustered):
mock_https.return_value = True
mock_unit_get.return_value = '1.2.3.4'
mock_determine_api_port.return_value = '12'
mock_determine_apache_port.return_value = '34'
mock_is_clustered.return_value = False
ctxt = contexts.ApacheSSLContext()
with patch.object(ctxt, 'enable_modules') as mock_enable_modules: