From 60fae562f9dfcb496d3a55289a1659a5bf75aae9 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Fri, 27 Jun 2014 13:19:01 +0100 Subject: [PATCH] Enable ssl support for neutron api --- config.yaml | 15 +++++++++++++++ hooks/neutron_api_context.py | 13 +++++++++++++ hooks/neutron_api_hooks.py | 22 ++++++++++++++++++++++ hooks/neutron_api_utils.py | 7 ++++++- unit_tests/test_neutron_api_hooks.py | 23 +++++++++++++++++++++-- unit_tests/test_neutron_api_utils.py | 6 +++++- 6 files changed, 82 insertions(+), 4 deletions(-) diff --git a/config.yaml b/config.yaml index 7be5a67d..451f87ed 100644 --- a/config.yaml +++ b/config.yaml @@ -89,3 +89,18 @@ options: default: False type: boolean description: Enable verbose logging + ssl_cert: + type: string + description: | + SSL certificate to install and use for API ports. Setting this value + and ssl_key will enable reverse proxying, point Nova's entry in the + Keystone catalog to use https, and override any certficiate and key + issued by Keystone (if it is configured to do so). + 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. diff --git a/hooks/neutron_api_context.py b/hooks/neutron_api_context.py index 6d612359..c2b40ef0 100644 --- a/hooks/neutron_api_context.py +++ b/hooks/neutron_api_context.py @@ -7,6 +7,19 @@ from charmhelpers.core.hookenv import ( from charmhelpers.contrib.openstack import context +class ApacheSSLContext(context.ApacheSSLContext): + + interfaces = ['https'] + external_ports = [] + service_namespace = 'neutron' + + def __call__(self): + # late import to work around circular dependency + from nova_cc_utils import determine_ports + self.external_ports = determine_ports() + return super(ApacheSSLContext, self).__call__() + + class IdentityServiceContext(context.IdentityServiceContext): def __call__(self): diff --git a/hooks/neutron_api_hooks.py b/hooks/neutron_api_hooks.py index 22d58eac..d40da3ec 100755 --- a/hooks/neutron_api_hooks.py +++ b/hooks/neutron_api_hooks.py @@ -2,6 +2,7 @@ import sys +from subprocess import check_call from charmhelpers.core.hookenv import ( Hooks, UnregisteredHookError, @@ -56,6 +57,25 @@ hooks = Hooks() CONFIGS = register_configs() +def configure_https(): + ''' + Enables SSL API Apache config if appropriate and kicks identity-service + with any required api updates. + ''' + # need to write all to ensure changes to the entire request pipeline + # propagate (c-api, haprxy, apache) + CONFIGS.write_all() + if 'https' in CONFIGS.complete_contexts(): + cmd = ['a2ensite', 'openstack_https_frontend'] + check_call(cmd) + else: + cmd = ['a2dissite', 'openstack_https_frontend'] + check_call(cmd) + + for rid in relation_ids('identity-service'): + identity_joined(rid=rid) + + @hooks.hook() def install(): execd_preinstall() @@ -72,6 +92,7 @@ def config_changed(): global CONFIGS if openstack_upgrade_available('neutron-server'): do_openstack_upgrade(CONFIGS) + configure_https() CONFIGS.write_all() for r_id in relation_ids('neutron-api'): neutron_api_relation_joined(rid=r_id) @@ -165,6 +186,7 @@ def identity_changed(): CONFIGS.write(NEUTRON_CONF) for r_id in relation_ids('neutron-api'): neutron_api_relation_joined(rid=r_id) + configure_https() @hooks.hook('neutron-api-relation-joined') diff --git a/hooks/neutron_api_utils.py b/hooks/neutron_api_utils.py index 6ddbfb76..be89705c 100644 --- a/hooks/neutron_api_utils.py +++ b/hooks/neutron_api_utils.py @@ -26,6 +26,8 @@ CLUSTER_RES = 'res_neutron_vip' # removed from original: charm-helper-sh BASE_PACKAGES = [ + 'apache2', + 'haproxy', 'python-keystoneclient', 'python-mysqldb', 'python-psycopg2', @@ -43,7 +45,6 @@ NEUTRON_CONF_DIR = "/etc/neutron" NEUTRON_CONF = '%s/neutron.conf' % NEUTRON_CONF_DIR HAPROXY_CONF = '/etc/haproxy/haproxy.cfg' -APACHE_CONF = '/etc/apache2/sites-available/openstack_https_frontend' APACHE_24_CONF = '/etc/apache2/sites-available/openstack_https_frontend.conf' NEUTRON_DEFAULT = '/etc/default/neutron-server' CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' @@ -65,6 +66,10 @@ BASE_RESOURCE_MAP = OrderedDict([ 'services': ['neutron-server'], 'contexts': [neutron_api_context.NeutronCCContext()], }), + (APACHE_24_CONF, { + 'contexts': [neutron_api_context.ApacheSSLContext()], + 'services': ['apache2'], + }), ]) diff --git a/unit_tests/test_neutron_api_hooks.py b/unit_tests/test_neutron_api_hooks.py index 1eabb79d..7ce7907e 100644 --- a/unit_tests/test_neutron_api_hooks.py +++ b/unit_tests/test_neutron_api_hooks.py @@ -24,6 +24,7 @@ TO_PATCH = [ 'canonical_url', 'config', 'CONFIGS', + 'check_call', 'configure_installation_source', 'determine_endpoints', 'determine_packages', @@ -82,7 +83,8 @@ class NeutronAPIHooksTests(CharmTestCase): self.open_port.assert_has_calls(_port_calls) self.assertTrue(self.execd_preinstall.called) - def test_config_changed(self): + @patch.object(hooks, 'configure_https') + def test_config_changed(self, conf_https): self.openstack_upgrade_available.return_value = True self.relation_ids.side_effect = self._fake_relids _n_api_rel_joined = self.patch('neutron_api_relation_joined') @@ -192,7 +194,8 @@ class NeutronAPIHooksTests(CharmTestCase): self._call_hook('identity-service-relation-changed') self.assertFalse(_api_rel_joined.called) - def test_identity_changed(self): + @patch.object(hooks, 'configure_https') + def test_identity_changed(self, conf_https): self.CONFIGS.complete_contexts.return_value = ['identity-service'] _api_rel_joined = self.patch('neutron_api_relation_joined') self.relation_ids.side_effect = self._fake_relids @@ -331,3 +334,19 @@ class NeutronAPIHooksTests(CharmTestCase): self._call_hook('ha-relation-changed') self.assertFalse(_n_api_rel_joined.called) self.assertFalse(_id_rel_joined.called) + + def test_configure_https(self): + self.CONFIGS.complete_contexts.return_value = ['https'] + self.relation_ids.side_effect = self._fake_relids + _id_rel_joined = self.patch('identity_joined') + hooks.configure_https() + self.check_call.assert_called_with(['a2ensite', 'openstack_https_frontend']) + self.assertTrue(_id_rel_joined.called) + + def test_configure_https_nohttps(self): + self.CONFIGS.complete_contexts.return_value = [] + self.relation_ids.side_effect = self._fake_relids + _id_rel_joined = self.patch('identity_joined') + hooks.configure_https() + self.check_call.assert_called_with(['a2dissite', 'openstack_https_frontend']) + self.assertTrue(_id_rel_joined.called) diff --git a/unit_tests/test_neutron_api_utils.py b/unit_tests/test_neutron_api_utils.py index ffb974f6..b9be2901 100644 --- a/unit_tests/test_neutron_api_utils.py +++ b/unit_tests/test_neutron_api_utils.py @@ -103,6 +103,9 @@ class TestNeutronAPIUtils(CharmTestCase): (ML2CONF, { 'services': ['neutron-server'], }), + (nutils.APACHE_24_CONF, { + 'services': ['apache2'], + }), ]) self.assertItemsEqual(_restart_map, expect) @@ -120,7 +123,8 @@ class TestNeutronAPIUtils(CharmTestCase): _regconfs = nutils.register_configs() confs = ['/etc/neutron/neutron.conf', '/etc/default/neutron-server', - '/etc/neutron/plugins/ml2/ml2_conf.ini'] + '/etc/neutron/plugins/ml2/ml2_conf.ini', + '/etc/apache2/sites-available/openstack_https_frontend.conf'] self.assertItemsEqual(_regconfs.configs, confs) @patch('os.path.isfile')