~raharper/charms/precise/swift-proxy/add-proxy-node-timeout

« back to all changes in this revision

Viewing changes to hooks/swift_utils.py

  • Committer: James Page
  • Date: 2013-06-03 16:50:28 UTC
  • mfrom: (35.1.18 swift-proxy)
  • Revision ID: james.page@canonical.com-20130603165028-f9ya6llay65w3t9e
* Python rewrite

* Support for manually assigned storage zones

* Support for high availability via hacluster subordinate.

* Adds Grizzly compatibility.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
import pwd
3
3
import subprocess
4
4
import lib.openstack_common as openstack
5
 
import utils
 
5
import lib.utils as utils
 
6
import lib.haproxy_utils as haproxy
 
7
import lib.apache_utils as apache
 
8
import lib.cluster_utils as cluster
 
9
import sys
 
10
from base64 import b64encode
 
11
 
6
12
 
7
13
# Various config files that are managed via templating.
8
 
SWIFT_HASH_FILE='/var/lib/juju/swift-hash-path.conf'
 
14
SWIFT_HASH_FILE = '/var/lib/juju/swift-hash-path.conf'
9
15
SWIFT_CONF = '/etc/swift/swift.conf'
10
16
SWIFT_PROXY_CONF = '/etc/swift/proxy-server.conf'
11
17
SWIFT_CONF_DIR = os.path.dirname(SWIFT_CONF)
32
38
    'python-keystone',
33
39
]
34
40
 
 
41
SWIFT_HA_RES = 'res_swift_vip'
 
42
 
35
43
# Folsom-specific packages
36
44
FOLSOM_PACKAGES = BASE_PACKAGES + ['swift-plugin-s3']
37
45
 
 
46
 
38
47
def proxy_control(action):
39
48
    '''utility to work around swift-init's bad RCs.'''
40
49
    def _cmd(action):
49
58
        elif status == 0:
50
59
            return subprocess.check_call(_cmd('stop'))
51
60
 
52
 
    # the proxy will not start unless there are balanced rings, gzip'd in /etc/swift
53
 
    missing=False
 
61
    # the proxy will not start unless there are balanced rings
 
62
    # gzip'd in /etc/swift
 
63
    missing = False
54
64
    for k in SWIFT_RINGS.keys():
55
65
        if not os.path.exists(os.path.join(SWIFT_CONF_DIR, '%s.ring.gz' % k)):
56
66
            missing = True
69
79
        elif status == 1:
70
80
            return subprocess.check_call(_cmd('start'))
71
81
 
 
82
 
72
83
def swift_user(username='swift'):
73
 
    user = pwd.getpwnam('swift')
 
84
    user = pwd.getpwnam(username)
74
85
    return (user.pw_uid, user.pw_gid)
75
86
 
76
87
 
105
116
    if os.path.isfile(SWIFT_HASH_FILE):
106
117
        with open(SWIFT_HASH_FILE, 'r') as hashfile:
107
118
            swift_hash = hashfile.read().strip()
 
119
    elif utils.config_get('swift-hash'):
 
120
        swift_hash = utils.config_get('swift-hash')
 
121
        with open(SWIFT_HASH_FILE, 'w') as hashfile:
 
122
            hashfile.write(swift_hash)
108
123
    else:
109
124
        cmd = ['od', '-t', 'x8', '-N', '8', '-A', 'n']
110
125
        rand = open('/dev/random', 'r')
148
163
                'keystone_host': utils.relation_get('auth_host',
149
164
                                                    unit, relid),
150
165
                'auth_port': utils.relation_get('auth_port', unit, relid),
151
 
                'service_user': utils.relation_get('service_username', unit, relid),
152
 
                'service_password': utils.relation_get('service_password', unit, relid),
153
 
                'service_tenant': utils.relation_get('service_tenant', unit, relid),
154
 
                'service_port': utils.relation_get('service_port', unit, relid),
155
 
                'admin_token': utils.relation_get('admin_token', unit, relid),
 
166
                'service_user': utils.relation_get('service_username',
 
167
                                                   unit, relid),
 
168
                'service_password': utils.relation_get('service_password',
 
169
                                                       unit, relid),
 
170
                'service_tenant': utils.relation_get('service_tenant',
 
171
                                                     unit, relid),
 
172
                'service_port': utils.relation_get('service_port',
 
173
                                                   unit, relid),
 
174
                'admin_token': utils.relation_get('admin_token',
 
175
                                                  unit, relid),
156
176
            }
157
177
            if None not in ks_auth.itervalues():
158
178
                return ks_auth
167
187
        import multiprocessing
168
188
        workers = multiprocessing.cpu_count()
169
189
 
 
190
    env_vars = {'OPENSTACK_SERVICE_SWIFT': 'proxy-server',
 
191
                'OPENSTACK_PORT_API': bind_port,
 
192
                'OPENSTACK_PORT_MEMCACHED': 11211}
 
193
    openstack.save_script_rc(**env_vars)
 
194
 
170
195
    ctxt = {
171
196
        'proxy_ip': utils.get_host_ip(),
172
 
        'bind_port': bind_port,
 
197
        'bind_port': cluster.determine_api_port(bind_port),
173
198
        'workers': workers,
174
 
        'operator_roles': utils.config_get('operator-roles')
 
199
        'operator_roles': utils.config_get('operator-roles'),
 
200
        'delay_auth_decision': utils.config_get('delay-auth-decision')
175
201
    }
176
202
 
177
 
    if utils.config_get('use-https') == 'no':
178
 
        ctxt['ssl'] = False
179
 
    else:
180
 
        ctxt['ssl'] = True
181
 
        ctxt['ssl_cert'] = SSL_CERT
182
 
        ctxt['ssl_key'] = SSL_KEY
 
203
    ctxt['ssl'] = False
183
204
 
184
205
    ks_auth = get_keystone_auth()
185
206
    if ks_auth:
193
214
    proxy_control('restart')
194
215
    subprocess.check_call(['open-port', str(bind_port)])
195
216
 
196
 
def configure_ssl():
197
 
    # this should be expanded to cover setting up user-specified certificates
198
 
    if (utils.config_get('use-https') == 'yes' and
199
 
        not os.path.isfile(SSL_CERT) and
200
 
        not os.path.isfile(SSL_KEY)):
201
 
        subj = '/C=%s/ST=%s/L=%s/CN=%s' %\
202
 
               (utils.config_get('country'), utils.config_get('state'),
203
 
                utils.config_get('locale'), utils.config_get('common-name'))
204
 
        cmd = ['openssl', 'req', '-new', '-x509', '-nodes',
205
 
               '-out', SSL_CERT, '-keyout', SSL_KEY,
206
 
               '-subj', subj]
207
 
        subprocess.check_call(cmd)
208
 
 
209
217
 
210
218
def _load_builder(path):
211
219
    # lifted straight from /usr/bin/swift-ring-builder
212
 
    from swift.common.ring import RingBuilder, Ring
 
220
    from swift.common.ring import RingBuilder
213
221
    import cPickle as pickle
214
222
    try:
215
223
        builder = pickle.load(open(path, 'rb'))
218
226
            builder = RingBuilder(1, 1, 1)
219
227
            builder.copy_from(builder_dict)
220
228
    except ImportError:  # Happens with really old builder pickles
221
 
        modules['swift.ring_builder'] = \
222
 
            modules['swift.common.ring.builder']
223
229
        builder = RingBuilder(1, 1, 1)
224
 
        builder.copy_from(pickle.load(open(argv[1], 'rb')))
 
230
        builder.copy_from(pickle.load(open(path, 'rb')))
225
231
    for dev in builder.devs:
226
232
        if dev and 'meta' not in dev:
227
233
            dev['meta'] = ''
233
239
    pickle.dump(ring.to_dict(), open(ring_path, 'wb'), protocol=2)
234
240
 
235
241
 
236
 
 
237
 
 
238
242
def ring_port(ring_path, node):
239
243
    '''determine correct port from relation settings for a given ring file.'''
240
244
    for name in ['account', 'object', 'container']:
248
252
    ring = RingBuilder(part_power, replicas, min_hours)
249
253
    _write_ring(ring, path)
250
254
 
 
255
 
251
256
def exists_in_ring(ring_path, node):
252
 
    from swift.common.ring import RingBuilder, Ring
253
257
    ring = _load_builder(ring_path).to_dict()
254
258
    node['port'] = ring_port(ring_path, node)
255
259
 
266
270
 
267
271
 
268
272
def add_to_ring(ring_path, node):
269
 
    from swift.common.ring import RingBuilder, Ring
270
273
    ring = _load_builder(ring_path)
271
274
    port = ring_port(ring_path, node)
272
275
 
286
289
    }
287
290
    ring.add_dev(new_dev)
288
291
    _write_ring(ring, ring_path)
289
 
    msg = 'Added new device to ring %s: %s' % (ring_path,
290
 
                                               [k for k in new_dev.iteritems()])
 
292
    msg = 'Added new device to ring %s: %s' %\
 
293
         (ring_path,
 
294
          [k for k in new_dev.iteritems()])
291
295
    utils.juju_log('INFO', msg)
292
296
 
293
297
 
332
336
            potential_zones.append(_get_zone(builder))
333
337
        return set(potential_zones).pop()
334
338
    else:
335
 
        utils.juju_log('Invalid zone assignment policy: %s' %\
336
 
                       assignemnt_policy)
 
339
        utils.juju_log('ERROR', 'Invalid zone assignment policy: %s' %\
 
340
                       assignment_policy)
337
341
        sys.exit(1)
338
342
 
339
343
 
351
355
        # swift-ring-builder returns 1 on WARNING (ring didn't require balance)
352
356
        return False
353
357
    else:
354
 
        utils.juju_log('balance_ring: %s returned %s' % (cmd, rc))
 
358
        utils.juju_log('ERROR', 'balance_ring: %s returned %s' % (cmd, rc))
355
359
        sys.exit(1)
356
360
 
 
361
 
357
362
def should_balance(rings):
358
363
    '''Based on zones vs min. replicas, determine whether or not the rings
359
364
       should be balanaced during initial configuration.'''
379
384
            host = utils.relation_get('private-address', unit, relid)
380
385
            allowed_hosts.append(utils.get_host_ip(host))
381
386
 
382
 
    ctxt = { 'www_dir': WWW_DIR, 'allowed_hosts': allowed_hosts }
 
387
    ctxt = {
 
388
        'www_dir': WWW_DIR,
 
389
        'allowed_hosts': allowed_hosts
 
390
        }
383
391
    with open(APACHE_CONF, 'w') as conf:
384
392
        conf.write(render_config(APACHE_CONF, ctxt))
385
 
    subprocess.check_call(['service', 'apache2', 'reload'])
386
 
 
 
393
    utils.reload('apache2')
 
394
 
 
395
 
 
396
def generate_cert():
 
397
    '''
 
398
    Generates a self signed certificate and key using the
 
399
    provided charm configuration data.
 
400
 
 
401
    returns: tuple of (cert, key)
 
402
    '''
 
403
    CERT = '/etc/swift/ssl.cert'
 
404
    KEY = '/etc/swift/ssl.key'
 
405
    if (not os.path.exists(CERT) and
 
406
        not os.path.exists(KEY)):
 
407
        subj = '/C=%s/ST=%s/L=%s/CN=%s' %\
 
408
            (utils.config_get('country'), utils.config_get('state'),
 
409
             utils.config_get('locale'), utils.config_get('common-name'))
 
410
        cmd = ['openssl', 'req', '-new', '-x509', '-nodes',
 
411
               '-out', CERT, '-keyout', KEY,
 
412
               '-subj', subj]
 
413
        subprocess.check_call(cmd)
 
414
        os.chmod(KEY, 0600)
 
415
    # Slurp as base64 encoded - makes handling easier up the stack
 
416
    with open(CERT, 'r') as cfile:
 
417
        ssl_cert = b64encode(cfile.read())
 
418
    with open(KEY, 'r') as kfile:
 
419
        ssl_key = b64encode(kfile.read())
 
420
    return (ssl_cert, ssl_key)
 
421
 
 
422
 
 
423
def configure_haproxy():
 
424
    api_port = utils.config_get('bind-port')
 
425
    service_ports = {
 
426
        "swift": [
 
427
            cluster.determine_haproxy_port(api_port),
 
428
            cluster.determine_api_port(api_port)
 
429
            ]
 
430
        }
 
431
    write_proxy_config()
 
432
    haproxy.configure_haproxy(service_ports)
 
433
 
 
434
 
 
435
def configure_https():
 
436
    if cluster.https():
 
437
        api_port = utils.config_get('bind-port')
 
438
        if (len(cluster.peer_units()) > 0 or
 
439
            cluster.is_clustered()):
 
440
            target_port = cluster.determine_haproxy_port(api_port)
 
441
            configure_haproxy()
 
442
        else:
 
443
            target_port = cluster.determine_api_port(api_port)
 
444
            write_proxy_config()
 
445
        cert, key = apache.get_cert()
 
446
        if None in (cert, key):
 
447
            cert, key = generate_cert()
 
448
        ca_cert = apache.get_ca_cert()
 
449
        apache.setup_https(namespace="swift",
 
450
                           port_maps={api_port: target_port},
 
451
                           cert=cert, key=key, ca_cert=ca_cert)
 
452
 
 
453
 
 
454
def do_openstack_upgrade(source, packages):
 
455
    openstack.configure_installation_source(source)
 
456
    os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
 
457
    subprocess.check_call(['apt-get', 'update'])
 
458
    cmd = ['apt-get', '--option', 'Dpkg::Options::=--force-confnew', '-y',
 
459
           'install'] + packages
 
460
    subprocess.check_call(cmd)