2
# Copyright 2012 Canonical Ltd.
4
# This file is sourced from lp:openstack-charm-helpers
7
# James Page <james.page@ubuntu.com>
8
# Adam Gandelman <adamg@ubuntu.com>
15
from subprocess import (
21
from charmhelpers.core.hookenv import (
29
from charmhelpers.core.host import (
38
KEYRING = '/etc/ceph/ceph.client.%s.keyring'
39
KEYFILE = '/etc/ceph/ceph.client.%s.key'
41
CEPH_CONF = """[global]
42
auth supported = %(auth)s
44
mon host = %(mon_hosts)s
49
# this local util can be dropped as soon the following branch lands
51
# https://code.launchpad.net/~gandelman-a/charm-helpers/service_running/
53
output = check_output(['service', service, 'status'])
54
except CalledProcessError:
57
if ("start/running" in output or "is running" in output):
64
ceph_dir = "/etc/ceph"
65
if not os.path.isdir(ceph_dir):
67
apt_install('ceph-common', fatal=True)
70
def rbd_exists(service, pool, rbd_img):
71
(rc, out) = commands.getstatusoutput('rbd list --id %s --pool %s' %
76
def create_rbd_image(service, pool, image, sizemb):
91
def pool_exists(service, name):
92
(rc, out) = commands.getstatusoutput("rados --id %s lspools" % service)
96
def create_pool(service, name):
107
def keyfile_path(service):
108
return KEYFILE % service
111
def keyring_path(service):
112
return KEYRING % service
115
def create_keyring(service, key):
116
keyring = keyring_path(service)
117
if os.path.exists(keyring):
118
log('ceph: Keyring exists at %s.' % keyring, level=INFO)
123
'--name=client.%s' % service,
127
log('ceph: Created new ring at %s.' % keyring, level=INFO)
130
def create_key_file(service, key):
131
# create a file containing the key
132
keyfile = keyfile_path(service)
133
if os.path.exists(keyfile):
134
log('ceph: Keyfile exists at %s.' % keyfile, level=INFO)
135
fd = open(keyfile, 'w')
138
log('ceph: Created new keyfile at %s.' % keyfile, level=INFO)
141
def get_ceph_nodes():
143
for r_id in relation_ids('ceph'):
144
for unit in related_units(r_id):
145
hosts.append(relation_get('private-address', unit=unit, rid=r_id))
149
def configure(service, key, auth):
150
create_keyring(service, key)
151
create_key_file(service, key)
152
hosts = get_ceph_nodes()
153
mon_hosts = ",".join(map(str, hosts))
154
keyring = keyring_path(service)
155
with open('/etc/ceph/ceph.conf', 'w') as ceph_conf:
156
ceph_conf.write(CEPH_CONF % locals())
157
modprobe_kernel_module('rbd')
160
def image_mapped(image_name):
161
(rc, out) = commands.getstatusoutput('rbd showmapped')
162
return image_name in out
165
def map_block_storage(service, pool, image):
169
'%s/%s' % (pool, image),
173
keyfile_path(service),
178
def filesystem_mounted(fs):
179
return fs in [f for m, f in mounts()]
182
def make_filesystem(blk_device, fstype='ext4'):
183
log('ceph: Formatting block device %s as filesystem %s.' %
184
(blk_device, fstype), level=INFO)
185
cmd = ['mkfs', '-t', fstype, blk_device]
189
def place_data_on_ceph(service, blk_device, data_src_dst, fstype='ext4'):
190
# mount block device into /mnt
191
mount(blk_device, '/mnt')
195
copy_files(data_src_dst, '/mnt')
199
# umount block device
202
_dir = os.stat(data_src_dst)
206
# re-mount where the data should originally be
207
mount(blk_device, data_src_dst, persist=True)
209
# ensure original ownership of new mount.
210
cmd = ['chown', '-R', '%s:%s' % (uid, gid), data_src_dst]
215
def modprobe_kernel_module(module):
216
log('ceph: Loading kernel module', level=INFO)
217
cmd = ['modprobe', module]
219
cmd = 'echo %s >> /etc/modules' % module
220
check_call(cmd, shell=True)
223
def copy_files(src, dst, symlinks=False, ignore=None):
224
for item in os.listdir(src):
225
s = os.path.join(src, item)
226
d = os.path.join(dst, item)
228
shutil.copytree(s, d, symlinks, ignore)
233
def ensure_ceph_storage(service, pool, rbd_img, sizemb, mount_point,
234
blk_device, fstype, system_services=[]):
236
To be called from the current cluster leader.
237
Ensures given pool and RBD image exists, is mapped to a block device,
238
and the device is formatted and mounted at the given mount_point.
240
If formatting a device for the first time, data existing at mount_point
241
will be migrated to the RBD device before being remounted.
243
All services listed in system_services will be stopped prior to data
244
migration and restarted when complete.
246
# Ensure pool, RBD image, RBD mappings are in place.
247
if not pool_exists(service, pool):
248
log('ceph: Creating new pool %s.' % pool, level=INFO)
249
create_pool(service, pool)
251
if not rbd_exists(service, pool, rbd_img):
252
log('ceph: Creating RBD image (%s).' % rbd_img, level=INFO)
253
create_rbd_image(service, pool, rbd_img, sizemb)
255
if not image_mapped(rbd_img):
256
log('ceph: Mapping RBD Image as a Block Device.', level=INFO)
257
map_block_storage(service, pool, rbd_img)
260
# TODO: What happens if for whatever reason this is run again and
261
# the data is already in the rbd device and/or is mounted??
262
# When it is mounted already, it will fail to make the fs
263
# XXX: This is really sketchy! Need to at least add an fstab entry
264
# otherwise this hook will blow away existing data if its executed
266
if not filesystem_mounted(mount_point):
267
make_filesystem(blk_device, fstype)
269
for svc in system_services:
271
log('Stopping services %s prior to migrating data.' % svc,
275
place_data_on_ceph(service, blk_device, mount_point, fstype)
277
for svc in system_services: