~gandelman-a/charms/precise/keystone/utils_sync

« back to all changes in this revision

Viewing changes to hooks/lib/apache_utils.py

  • Committer: Adam Gandelman
  • Date: 2013-03-19 18:06:25 UTC
  • mfrom: (57.1.25 keystone)
  • Revision ID: adamg@canonical.com-20130319180625-ov50yaiiec32s15c
Big refactor and cleanup from James.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# Copyright 2012 Canonical Ltd.
 
3
#
 
4
# This file is sourced from lp:openstack-charm-helpers
 
5
#
 
6
# Authors:
 
7
#  James Page <james.page@ubuntu.com>
 
8
#  Adam Gandelman <adamg@ubuntu.com>
 
9
#
 
10
 
 
11
from lib.utils import (
 
12
    relation_ids,
 
13
    relation_list,
 
14
    relation_get,
 
15
    render_template,
 
16
    juju_log,
 
17
    config_get,
 
18
    install,
 
19
    get_host_ip,
 
20
    restart
 
21
    )
 
22
from lib.cluster_utils import https
 
23
 
 
24
import os
 
25
import subprocess
 
26
from base64 import b64decode
 
27
 
 
28
APACHE_SITE_DIR = "/etc/apache2/sites-available"
 
29
SITE_TEMPLATE = "apache2_site.tmpl"
 
30
RELOAD_CHECK = "To activate the new configuration"
 
31
 
 
32
 
 
33
def get_cert():
 
34
    cert = config_get('ssl_cert')
 
35
    key = config_get('ssl_key')
 
36
    if not (cert and key):
 
37
        juju_log('INFO',
 
38
                 "Inspecting identity-service relations for SSL certificate.")
 
39
        cert = key = None
 
40
        for r_id in relation_ids('identity-service'):
 
41
            for unit in relation_list(r_id):
 
42
                if not cert:
 
43
                    cert = relation_get('ssl_cert',
 
44
                                        rid=r_id, unit=unit)
 
45
                if not key:
 
46
                    key = relation_get('ssl_key',
 
47
                                       rid=r_id, unit=unit)
 
48
    return (cert, key)
 
49
 
 
50
 
 
51
def get_ca_cert():
 
52
    ca_cert = None
 
53
    juju_log('INFO',
 
54
             "Inspecting identity-service relations for CA SSL certificate.")
 
55
    for r_id in relation_ids('identity-service'):
 
56
        for unit in relation_list(r_id):
 
57
            if not ca_cert:
 
58
                ca_cert = relation_get('ca_cert',
 
59
                                       rid=r_id, unit=unit)
 
60
    return ca_cert
 
61
 
 
62
 
 
63
def install_ca_cert(ca_cert):
 
64
    if ca_cert:
 
65
        with open('/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt',
 
66
                  'w') as crt:
 
67
            crt.write(ca_cert)
 
68
        subprocess.check_call(['update-ca-certificates', '--fresh'])
 
69
 
 
70
 
 
71
def enable_https(port_maps, namespace, cert, key, ca_cert=None):
 
72
    '''
 
73
    For a given number of port mappings, configures apache2
 
74
    HTTPs local reverse proxying using certficates and keys provided in
 
75
    either configuration data (preferred) or relation data.  Assumes ports
 
76
    are not in use (calling charm should ensure that).
 
77
 
 
78
    port_maps: dict: external to internal port mappings
 
79
    namespace: str: name of charm
 
80
    '''
 
81
    def _write_if_changed(path, new_content):
 
82
        content = None
 
83
        if os.path.exists(path):
 
84
            with open(path, 'r') as f:
 
85
                content = f.read().strip()
 
86
        if content != new_content:
 
87
            with open(path, 'w') as f:
 
88
                f.write(new_content)
 
89
            return True
 
90
        else:
 
91
            return False
 
92
 
 
93
    juju_log('INFO', "Enabling HTTPS for port mappings: {}".format(port_maps))
 
94
    http_restart = False
 
95
 
 
96
    if cert:
 
97
        cert = b64decode(cert)
 
98
    if key:
 
99
        key = b64decode(key)
 
100
    if ca_cert:
 
101
        ca_cert = b64decode(ca_cert)
 
102
 
 
103
    if not cert and not key:
 
104
        juju_log('ERROR',
 
105
                 "Expected but could not find SSL certificate data, not "
 
106
                 "configuring HTTPS!")
 
107
        return False
 
108
 
 
109
    install('apache2')
 
110
    if RELOAD_CHECK in subprocess.check_output(['a2enmod', 'ssl',
 
111
                                                'proxy', 'proxy_http']):
 
112
        http_restart = True
 
113
 
 
114
    ssl_dir = os.path.join('/etc/apache2/ssl', namespace)
 
115
    if not os.path.exists(ssl_dir):
 
116
        os.makedirs(ssl_dir)
 
117
 
 
118
    if (_write_if_changed(os.path.join(ssl_dir, 'cert'), cert)):
 
119
        http_restart = True
 
120
    if (_write_if_changed(os.path.join(ssl_dir, 'key'), key)):
 
121
        http_restart = True
 
122
    os.chmod(os.path.join(ssl_dir, 'key'), 0600)
 
123
 
 
124
    install_ca_cert(ca_cert)
 
125
 
 
126
    sites_dir = '/etc/apache2/sites-available'
 
127
    for ext_port, int_port in port_maps.items():
 
128
        juju_log('INFO',
 
129
                 'Creating apache2 reverse proxy vhost'
 
130
                 ' for {}:{}'.format(ext_port,
 
131
                                     int_port))
 
132
        site = "{}_{}".format(namespace, ext_port)
 
133
        site_path = os.path.join(sites_dir, site)
 
134
        with open(site_path, 'w') as fsite:
 
135
            context = {
 
136
                "ext": ext_port,
 
137
                "int": int_port,
 
138
                "namespace": namespace,
 
139
                "private_address": get_host_ip()
 
140
                }
 
141
            fsite.write(render_template(SITE_TEMPLATE,
 
142
                                        context))
 
143
 
 
144
        if RELOAD_CHECK in subprocess.check_output(['a2ensite', site]):
 
145
            http_restart = True
 
146
 
 
147
    if http_restart:
 
148
        restart('apache2')
 
149
 
 
150
    return True
 
151
 
 
152
 
 
153
def disable_https(port_maps, namespace):
 
154
    '''
 
155
    Ensure HTTPS reverse proxying is disables for given port mappings
 
156
 
 
157
    port_maps: dict: of ext -> int port mappings
 
158
    namespace: str: name of chamr
 
159
    '''
 
160
    juju_log('INFO', 'Ensuring HTTPS disabled for {}'.format(port_maps))
 
161
 
 
162
    if (not os.path.exists('/etc/apache2') or
 
163
        not os.path.exists(os.path.join('/etc/apache2/ssl', namespace))):
 
164
        return
 
165
 
 
166
    http_restart = False
 
167
    for ext_port in port_maps.keys():
 
168
        if os.path.exists(os.path.join(APACHE_SITE_DIR,
 
169
                                       "{}_{}".format(namespace,
 
170
                                                      ext_port))):
 
171
            juju_log('INFO',
 
172
                     "Disabling HTTPS reverse proxy"
 
173
                     " for {} {}.".format(namespace,
 
174
                                          ext_port))
 
175
            if (RELOAD_CHECK in
 
176
                subprocess.check_output(['a2dissite',
 
177
                                         '{}_{}'.format(namespace,
 
178
                                                        ext_port)])):
 
179
                http_restart = True
 
180
 
 
181
    if http_restart:
 
182
        restart(['apache2'])
 
183
 
 
184
 
 
185
def setup_https(port_maps, namespace, cert, key, ca_cert=None):
 
186
    '''
 
187
    Ensures HTTPS is either enabled or disabled for given port
 
188
    mapping.
 
189
 
 
190
    port_maps: dict: of ext -> int port mappings
 
191
    namespace: str: name of charm
 
192
    '''
 
193
    if not https:
 
194
        disable_https(port_maps, namespace)
 
195
    else:
 
196
        enable_https(port_maps, namespace, cert, key, ca_cert)