~niedbalski/charms/trusty/swift-proxy/fix-lp-1308557

« back to all changes in this revision

Viewing changes to hooks/swift_context.py

  • Committer: James Page
  • Date: 2013-10-15 11:46:42 UTC
  • mfrom: (44.1.13 swift-proxy)
  • Revision ID: james.page@canonical.com-20131015114642-b2dq2fessa9jvoa5
Update of all Havana / Saucy / python-redux work:

* Full python rewrite using new OpenStack charm-helpers.
* Havana support

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from charmhelpers.core.hookenv import (
 
2
    config,
 
3
    log,
 
4
    relation_ids,
 
5
    related_units,
 
6
    relation_get,
 
7
    unit_get
 
8
)
 
9
 
 
10
from charmhelpers.contrib.openstack.context import (
 
11
    OSContextGenerator,
 
12
    ApacheSSLContext as SSLContext,
 
13
    context_complete,
 
14
    CA_CERT_PATH
 
15
)
 
16
 
 
17
from charmhelpers.contrib.hahelpers.cluster import (
 
18
    determine_api_port,
 
19
    determine_haproxy_port,
 
20
)
 
21
 
 
22
from charmhelpers.contrib.openstack.utils import get_host_ip
 
23
import subprocess
 
24
import os
 
25
 
 
26
 
 
27
from charmhelpers.contrib.hahelpers.apache import (
 
28
    get_cert,
 
29
    get_ca_cert,
 
30
)
 
31
 
 
32
from base64 import b64decode, b64encode
 
33
 
 
34
 
 
35
class HAProxyContext(OSContextGenerator):
 
36
    interfaces = ['cluster']
 
37
 
 
38
    def __call__(self):
 
39
        '''
 
40
        Extends the main charmhelpers HAProxyContext with a port mapping
 
41
        specific to this charm.
 
42
        Also used to extend cinder.conf context with correct api_listening_port
 
43
        '''
 
44
        haproxy_port = determine_haproxy_port(config('bind-port'))
 
45
        api_port = determine_api_port(config('bind-port'))
 
46
 
 
47
        ctxt = {
 
48
            'service_ports': {'swift_api': [haproxy_port, api_port]},
 
49
        }
 
50
        return ctxt
 
51
 
 
52
 
 
53
WWW_DIR = '/var/www/swift-rings'
 
54
 
 
55
 
 
56
def generate_cert():
 
57
    '''
 
58
    Generates a self signed certificate and key using the
 
59
    provided charm configuration data.
 
60
 
 
61
    returns: tuple of (cert, key)
 
62
    '''
 
63
    CERT = '/etc/swift/ssl.cert'
 
64
    KEY = '/etc/swift/ssl.key'
 
65
    if not os.path.exists(CERT) and not os.path.exists(KEY):
 
66
        subj = '/C=%s/ST=%s/L=%s/CN=%s' %\
 
67
            (config('country'), config('state'),
 
68
             config('locale'), config('common-name'))
 
69
        cmd = ['openssl', 'req', '-new', '-x509', '-nodes',
 
70
               '-out', CERT, '-keyout', KEY,
 
71
               '-subj', subj]
 
72
        subprocess.check_call(cmd)
 
73
        os.chmod(KEY, 0600)
 
74
    # Slurp as base64 encoded - makes handling easier up the stack
 
75
    with open(CERT, 'r') as cfile:
 
76
        ssl_cert = b64encode(cfile.read())
 
77
    with open(KEY, 'r') as kfile:
 
78
        ssl_key = b64encode(kfile.read())
 
79
    return (ssl_cert, ssl_key)
 
80
 
 
81
 
 
82
class ApacheSSLContext(SSLContext):
 
83
    interfaces = ['https']
 
84
    external_ports = [config('bind-port')]
 
85
    service_namespace = 'swift'
 
86
 
 
87
    def configure_cert(self):
 
88
        if not os.path.isdir('/etc/apache2/ssl'):
 
89
            os.mkdir('/etc/apache2/ssl')
 
90
        ssl_dir = os.path.join('/etc/apache2/ssl/', self.service_namespace)
 
91
        if not os.path.isdir(ssl_dir):
 
92
            os.mkdir(ssl_dir)
 
93
        cert, key = get_cert()
 
94
        # Swift specific - generate a cert by default if not using
 
95
        # a) user supplied cert or b) keystone signed cert
 
96
        if None in [cert, key]:
 
97
            cert, key = generate_cert()
 
98
        with open(os.path.join(ssl_dir, 'cert'), 'w') as cert_out:
 
99
            cert_out.write(b64decode(cert))
 
100
        with open(os.path.join(ssl_dir, 'key'), 'w') as key_out:
 
101
            key_out.write(b64decode(key))
 
102
        ca_cert = get_ca_cert()
 
103
        if ca_cert:
 
104
            with open(CA_CERT_PATH, 'w') as ca_out:
 
105
                ca_out.write(b64decode(ca_cert))
 
106
            subprocess.check_call(['update-ca-certificates'])
 
107
 
 
108
    def __call__(self):
 
109
        return super(ApacheSSLContext, self).__call__()
 
110
 
 
111
 
 
112
class SwiftRingContext(OSContextGenerator):
 
113
    def __call__(self):
 
114
        allowed_hosts = []
 
115
        for relid in relation_ids('swift-storage'):
 
116
            for unit in related_units(relid):
 
117
                host = relation_get('private-address', unit, relid)
 
118
                allowed_hosts.append(get_host_ip(host))
 
119
 
 
120
        ctxt = {
 
121
            'www_dir': WWW_DIR,
 
122
            'allowed_hosts': allowed_hosts
 
123
        }
 
124
        return ctxt
 
125
 
 
126
 
 
127
class SwiftIdentityContext(OSContextGenerator):
 
128
    interfaces = ['identity-service']
 
129
 
 
130
    def __call__(self):
 
131
        bind_port = config('bind-port')
 
132
        workers = config('workers')
 
133
        if workers == '0':
 
134
            import multiprocessing
 
135
            workers = multiprocessing.cpu_count()
 
136
        ctxt = {
 
137
            'proxy_ip': get_host_ip(unit_get('private-address')),
 
138
            'bind_port': determine_api_port(bind_port),
 
139
            'workers': workers,
 
140
            'operator_roles': config('operator-roles'),
 
141
            'delay_auth_decision': config('delay-auth-decision')
 
142
        }
 
143
 
 
144
        ctxt['ssl'] = False
 
145
 
 
146
        auth_type = config('auth-type')
 
147
        auth_host = config('keystone-auth-host')
 
148
        admin_user = config('keystone-admin-user')
 
149
        admin_password = config('keystone-admin-user')
 
150
        if (auth_type == 'keystone' and auth_host
 
151
            and admin_user and admin_password):
 
152
            log('Using user-specified Keystone configuration.')
 
153
            ks_auth = {
 
154
                'auth_type': 'keystone',
 
155
                'auth_protocol': config('keystone-auth-protocol'),
 
156
                'keystone_host': auth_host,
 
157
                'auth_port': config('keystone-auth-port'),
 
158
                'service_user': admin_user,
 
159
                'service_password': admin_password,
 
160
                'service_tenant': config('keystone-admin-tenant-name')
 
161
            }
 
162
            ctxt.update(ks_auth)
 
163
 
 
164
        for relid in relation_ids('identity-service'):
 
165
            log('Using Keystone configuration from identity-service.')
 
166
            for unit in related_units(relid):
 
167
                ks_auth = {
 
168
                    'auth_type': 'keystone',
 
169
                    'auth_protocol': 'http',  # TODO: http hardcode
 
170
                    'keystone_host': relation_get('auth_host',
 
171
                                                  unit, relid),
 
172
                    'auth_port': relation_get('auth_port',
 
173
                                              unit, relid),
 
174
                    'service_user': relation_get('service_username',
 
175
                                                 unit, relid),
 
176
                    'service_password': relation_get('service_password',
 
177
                                                     unit, relid),
 
178
                    'service_tenant': relation_get('service_tenant',
 
179
                                                   unit, relid),
 
180
                    'service_port': relation_get('service_port',
 
181
                                                 unit, relid),
 
182
                    'admin_token': relation_get('admin_token',
 
183
                                                unit, relid),
 
184
                }
 
185
                if context_complete(ks_auth):
 
186
                    ctxt.update(ks_auth)
 
187
        return ctxt
 
188
 
 
189
 
 
190
class MemcachedContext(OSContextGenerator):
 
191
    def __call__(self):
 
192
        ctxt = {
 
193
            'proxy_ip': get_host_ip(unit_get('private-address'))
 
194
        }
 
195
        return ctxt
 
196
 
 
197
SWIFT_HASH_FILE = '/var/lib/juju/swift-hash-path.conf'
 
198
 
 
199
 
 
200
def get_swift_hash():
 
201
    if os.path.isfile(SWIFT_HASH_FILE):
 
202
        with open(SWIFT_HASH_FILE, 'r') as hashfile:
 
203
            swift_hash = hashfile.read().strip()
 
204
    elif config('swift-hash'):
 
205
        swift_hash = config('swift-hash')
 
206
        with open(SWIFT_HASH_FILE, 'w') as hashfile:
 
207
            hashfile.write(swift_hash)
 
208
    else:
 
209
        cmd = ['od', '-t', 'x8', '-N', '8', '-A', 'n']
 
210
        rand = open('/dev/random', 'r')
 
211
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=rand)
 
212
        swift_hash = p.communicate()[0].strip()
 
213
        with open(SWIFT_HASH_FILE, 'w') as hashfile:
 
214
            hashfile.write(swift_hash)
 
215
    return swift_hash
 
216
 
 
217
 
 
218
class SwiftHashContext(OSContextGenerator):
 
219
    def __call__(self):
 
220
        ctxt = {
 
221
            'swift_hash': get_swift_hash()
 
222
        }
 
223
        return ctxt