~negronjl/charms/precise/mysql/mysql-file-permissions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#
# Copyright 2012 Canonical Ltd.
#
# This file is sourced from lp:openstack-charm-helpers
#
# Authors:
#  James Page <james.page@ubuntu.com>
#  Adam Gandelman <adamg@ubuntu.com>
#

import commands
import subprocess
import os
import shutil
import time
import lib.utils as utils

KEYRING = '/etc/ceph/ceph.client.%s.keyring'
KEYFILE = '/etc/ceph/ceph.client.%s.key'

CEPH_CONF = """[global]
 auth supported = %(auth)s
 keyring = %(keyring)s
 mon host = %(mon_hosts)s
"""


def execute(cmd):
    subprocess.check_call(cmd)


def execute_shell(cmd):
    subprocess.check_call(cmd, shell=True)


def install():
    ceph_dir = "/etc/ceph"
    if not os.path.isdir(ceph_dir):
        os.mkdir(ceph_dir)
    utils.install('ceph-common')


def rbd_exists(service, pool, rbd_img):
    (rc, out) = commands.getstatusoutput('rbd list --id %s --pool %s' %\
                                         (service, pool))
    return rbd_img in out


def create_rbd_image(service, pool, image, sizemb):
    cmd = [
        'rbd',
        'create',
        image,
        '--size',
        str(sizemb),
        '--id',
        service,
        '--pool',
        pool
        ]
    execute(cmd)


def pool_exists(service, name):
    (rc, out) = commands.getstatusoutput("rados --id %s lspools" % service)
    return name in out


def create_pool(service, name):
    cmd = [
        'rados',
        '--id',
        service,
        'mkpool',
        name
        ]
    execute(cmd)


def keyfile_path(service):
    return KEYFILE % service


def keyring_path(service):
    return KEYRING % service


def create_keyring(service, key):
    keyring = keyring_path(service)
    if os.path.exists(keyring):
        utils.juju_log('INFO', 'ceph: Keyring exists at %s.' % keyring)
    cmd = [
        'ceph-authtool',
        keyring,
        '--create-keyring',
        '--name=client.%s' % service,
        '--add-key=%s' % key
        ]
    execute(cmd)
    utils.juju_log('INFO', 'ceph: Created new ring at %s.' % keyring)


def create_key_file(service, key):
    # create a file containing the key
    keyfile = keyfile_path(service)
    if os.path.exists(keyfile):
        utils.juju_log('INFO', 'ceph: Keyfile exists at %s.' % keyfile)
    fd = open(keyfile, 'w')
    fd.write(key)
    fd.close()
    utils.juju_log('INFO', 'ceph: Created new keyfile at %s.' % keyfile)


def get_ceph_nodes():
    hosts = []
    for r_id in utils.relation_ids('ceph'):
        for unit in utils.relation_list(r_id):
            hosts.append(utils.relation_get('private-address',
                                            unit=unit, rid=r_id))
    return hosts


def configure(service, key, auth):
    create_keyring(service, key)
    create_key_file(service, key)
    hosts = get_ceph_nodes()
    mon_hosts = ",".join(map(str, hosts))
    keyring = keyring_path(service)
    with open('/etc/ceph/ceph.conf', 'w') as ceph_conf:
        ceph_conf.write(CEPH_CONF % locals())
    modprobe_kernel_module('rbd')


def image_mapped(image_name):
    (rc, out) = commands.getstatusoutput('rbd showmapped')
    return image_name in out


def map_block_storage(service, pool, image):
    cmd = [
        'rbd',
        'map',
        '%s/%s' % (pool, image),
        '--user',
        service,
        '--secret',
        keyfile_path(service),
        ]
    execute(cmd)


def filesystem_mounted(fs):
    return subprocess.call(['grep', '-wqs', fs, '/proc/mounts']) == 0


def make_filesystem(blk_device, fstype='ext4'):
    count = 0
    e_noent = os.errno.ENOENT
    while not os.path.exists(blk_device):
        if count >= 10:
            utils.juju_log('ERROR',
                'ceph: gave up waiting on block device %s' % blk_device)
            raise IOError(e_noent, os.strerror(e_noent), blk_device)
        utils.juju_log('INFO',
            'ceph: waiting for block device %s to appear' % blk_device)
        count += 1
        time.sleep(1)
    else:
        utils.juju_log('INFO',
            'ceph: Formatting block device %s as filesystem %s.' %
            (blk_device, fstype))
        execute(['mkfs', '-t', fstype, blk_device])


def place_data_on_ceph(service, blk_device, data_src_dst, fstype='ext4'):
    # mount block device into /mnt
    cmd = ['mount', '-t', fstype, blk_device, '/mnt']
    execute(cmd)

    # copy data to /mnt
    try:
        copy_files(data_src_dst, '/mnt')
    except:
        pass

    # umount block device
    cmd = ['umount', '/mnt']
    execute(cmd)

    _dir = os.stat(data_src_dst)
    uid = _dir.st_uid
    gid = _dir.st_gid

    # re-mount where the data should originally be
    cmd = ['mount', '-t', fstype, blk_device, data_src_dst]
    execute(cmd)

    # ensure original ownership of new mount.
    cmd = ['chown', '-R', '%s:%s' % (uid, gid), data_src_dst]
    execute(cmd)


# TODO: re-use
def modprobe_kernel_module(module):
    utils.juju_log('INFO', 'Loading kernel module')
    cmd = ['modprobe', module]
    execute(cmd)
    cmd = 'echo %s >> /etc/modules' % module
    execute_shell(cmd)


def copy_files(src, dst, symlinks=False, ignore=None):
    for item in os.listdir(src):
        s = os.path.join(src, item)
        d = os.path.join(dst, item)
        if os.path.isdir(s):
            shutil.copytree(s, d, symlinks, ignore)
        else:
            shutil.copy2(s, d)


def ensure_ceph_storage(service, pool, rbd_img, sizemb, mount_point,
                        blk_device, fstype, system_services=[]):
    """
    To be called from the current cluster leader.
    Ensures given pool and RBD image exists, is mapped to a block device,
    and the device is formatted and mounted at the given mount_point.

    If formatting a device for the first time, data existing at mount_point
    will be migrated to the RBD device before being remounted.

    All services listed in system_services will be stopped prior to data
    migration and restarted when complete.
    """
    # Ensure pool, RBD image, RBD mappings are in place.
    if not pool_exists(service, pool):
        utils.juju_log('INFO', 'ceph: Creating new pool %s.' % pool)
        create_pool(service, pool)

    if not rbd_exists(service, pool, rbd_img):
        utils.juju_log('INFO', 'ceph: Creating RBD image (%s).' % rbd_img)
        create_rbd_image(service, pool, rbd_img, sizemb)

    if not image_mapped(rbd_img):
        utils.juju_log('INFO', 'ceph: Mapping RBD Image as a Block Device.')
        map_block_storage(service, pool, rbd_img)

    # make file system
    # TODO: What happens if for whatever reason this is run again and
    # the data is already in the rbd device and/or is mounted??
    # When it is mounted already, it will fail to make the fs
    # XXX: This is really sketchy!  Need to at least add an fstab entry
    #      otherwise this hook will blow away existing data if its executed
    #      after a reboot.
    if not filesystem_mounted(mount_point):
        make_filesystem(blk_device, fstype)

        for svc in system_services:
            if utils.running(svc):
                utils.juju_log('INFO',
                               'Stopping services %s prior to migrating '\
                               'data' % svc)
                utils.stop(svc)

        place_data_on_ceph(service, blk_device, mount_point, fstype)

        for svc in system_services:
            utils.start(svc)