github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/acceptancetests/repository/xenial/mysql/hooks/lib/ceph_utils.py (about) 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 import commands 12 import subprocess 13 import os 14 import shutil 15 import time 16 import lib.utils as utils 17 18 KEYRING = '/etc/ceph/ceph.client.%s.keyring' 19 KEYFILE = '/etc/ceph/ceph.client.%s.key' 20 21 CEPH_CONF = """[global] 22 auth supported = %(auth)s 23 keyring = %(keyring)s 24 mon host = %(mon_hosts)s 25 """ 26 27 28 def execute(cmd): 29 subprocess.check_call(cmd) 30 31 32 def execute_shell(cmd): 33 subprocess.check_call(cmd, shell=True) 34 35 36 def install(): 37 ceph_dir = "/etc/ceph" 38 if not os.path.isdir(ceph_dir): 39 os.mkdir(ceph_dir) 40 utils.install('ceph-common') 41 42 43 def rbd_exists(service, pool, rbd_img): 44 (rc, out) = commands.getstatusoutput('rbd list --id %s --pool %s' %\ 45 (service, pool)) 46 return rbd_img in out 47 48 49 def create_rbd_image(service, pool, image, sizemb): 50 cmd = [ 51 'rbd', 52 'create', 53 image, 54 '--size', 55 str(sizemb), 56 '--id', 57 service, 58 '--pool', 59 pool 60 ] 61 execute(cmd) 62 63 64 def pool_exists(service, name): 65 (rc, out) = commands.getstatusoutput("rados --id %s lspools" % service) 66 return name in out 67 68 69 def create_pool(service, name): 70 cmd = [ 71 'rados', 72 '--id', 73 service, 74 'mkpool', 75 name 76 ] 77 execute(cmd) 78 79 80 def keyfile_path(service): 81 return KEYFILE % service 82 83 84 def keyring_path(service): 85 return KEYRING % service 86 87 88 def create_keyring(service, key): 89 keyring = keyring_path(service) 90 if os.path.exists(keyring): 91 utils.juju_log('INFO', 'ceph: Keyring exists at %s.' % keyring) 92 cmd = [ 93 'ceph-authtool', 94 keyring, 95 '--create-keyring', 96 '--name=client.%s' % service, 97 '--add-key=%s' % key 98 ] 99 execute(cmd) 100 utils.juju_log('INFO', 'ceph: Created new ring at %s.' % keyring) 101 102 103 def create_key_file(service, key): 104 # create a file containing the key 105 keyfile = keyfile_path(service) 106 if os.path.exists(keyfile): 107 utils.juju_log('INFO', 'ceph: Keyfile exists at %s.' % keyfile) 108 fd = open(keyfile, 'w') 109 fd.write(key) 110 fd.close() 111 utils.juju_log('INFO', 'ceph: Created new keyfile at %s.' % keyfile) 112 113 114 def get_ceph_nodes(): 115 hosts = [] 116 for r_id in utils.relation_ids('ceph'): 117 for unit in utils.relation_list(r_id): 118 hosts.append(utils.relation_get('private-address', 119 unit=unit, rid=r_id)) 120 return hosts 121 122 123 def configure(service, key, auth): 124 create_keyring(service, key) 125 create_key_file(service, key) 126 hosts = get_ceph_nodes() 127 mon_hosts = ",".join(map(str, hosts)) 128 keyring = keyring_path(service) 129 with open('/etc/ceph/ceph.conf', 'w') as ceph_conf: 130 ceph_conf.write(CEPH_CONF % locals()) 131 modprobe_kernel_module('rbd') 132 133 134 def image_mapped(image_name): 135 (rc, out) = commands.getstatusoutput('rbd showmapped') 136 return image_name in out 137 138 139 def map_block_storage(service, pool, image): 140 cmd = [ 141 'rbd', 142 'map', 143 '%s/%s' % (pool, image), 144 '--user', 145 service, 146 '--secret', 147 keyfile_path(service), 148 ] 149 execute(cmd) 150 151 152 def filesystem_mounted(fs): 153 return subprocess.call(['grep', '-wqs', fs, '/proc/mounts']) == 0 154 155 156 def make_filesystem(blk_device, fstype='ext4'): 157 count = 0 158 e_noent = os.errno.ENOENT 159 while not os.path.exists(blk_device): 160 if count >= 10: 161 utils.juju_log('ERROR', 162 'ceph: gave up waiting on block device %s' % blk_device) 163 raise IOError(e_noent, os.strerror(e_noent), blk_device) 164 utils.juju_log('INFO', 165 'ceph: waiting for block device %s to appear' % blk_device) 166 count += 1 167 time.sleep(1) 168 else: 169 utils.juju_log('INFO', 170 'ceph: Formatting block device %s as filesystem %s.' % 171 (blk_device, fstype)) 172 execute(['mkfs', '-t', fstype, blk_device]) 173 174 175 def place_data_on_ceph(service, blk_device, data_src_dst, fstype='ext4'): 176 # mount block device into /mnt 177 cmd = ['mount', '-t', fstype, blk_device, '/mnt'] 178 execute(cmd) 179 180 # copy data to /mnt 181 try: 182 copy_files(data_src_dst, '/mnt') 183 except: 184 pass 185 186 # umount block device 187 cmd = ['umount', '/mnt'] 188 execute(cmd) 189 190 _dir = os.stat(data_src_dst) 191 uid = _dir.st_uid 192 gid = _dir.st_gid 193 194 # re-mount where the data should originally be 195 cmd = ['mount', '-t', fstype, blk_device, data_src_dst] 196 execute(cmd) 197 198 # ensure original ownership of new mount. 199 cmd = ['chown', '-R', '%s:%s' % (uid, gid), data_src_dst] 200 execute(cmd) 201 202 203 # TODO: re-use 204 def modprobe_kernel_module(module): 205 utils.juju_log('INFO', 'Loading kernel module') 206 cmd = ['modprobe', module] 207 execute(cmd) 208 cmd = 'echo %s >> /etc/modules' % module 209 execute_shell(cmd) 210 211 212 def copy_files(src, dst, symlinks=False, ignore=None): 213 for item in os.listdir(src): 214 s = os.path.join(src, item) 215 d = os.path.join(dst, item) 216 if os.path.isdir(s): 217 shutil.copytree(s, d, symlinks, ignore) 218 else: 219 shutil.copy2(s, d) 220 221 222 def ensure_ceph_storage(service, pool, rbd_img, sizemb, mount_point, 223 blk_device, fstype, system_services=[]): 224 """ 225 To be called from the current cluster leader. 226 Ensures given pool and RBD image exists, is mapped to a block device, 227 and the device is formatted and mounted at the given mount_point. 228 229 If formatting a device for the first time, data existing at mount_point 230 will be migrated to the RBD device before being remounted. 231 232 All services listed in system_services will be stopped prior to data 233 migration and restarted when complete. 234 """ 235 # Ensure pool, RBD image, RBD mappings are in place. 236 if not pool_exists(service, pool): 237 utils.juju_log('INFO', 'ceph: Creating new pool %s.' % pool) 238 create_pool(service, pool) 239 240 if not rbd_exists(service, pool, rbd_img): 241 utils.juju_log('INFO', 'ceph: Creating RBD image (%s).' % rbd_img) 242 create_rbd_image(service, pool, rbd_img, sizemb) 243 244 if not image_mapped(rbd_img): 245 utils.juju_log('INFO', 'ceph: Mapping RBD Image as a Block Device.') 246 map_block_storage(service, pool, rbd_img) 247 248 # make file system 249 # TODO: What happens if for whatever reason this is run again and 250 # the data is already in the rbd device and/or is mounted?? 251 # When it is mounted already, it will fail to make the fs 252 # XXX: This is really sketchy! Need to at least add an fstab entry 253 # otherwise this hook will blow away existing data if its executed 254 # after a reboot. 255 if not filesystem_mounted(mount_point): 256 make_filesystem(blk_device, fstype) 257 258 for svc in system_services: 259 if utils.running(svc): 260 utils.juju_log('INFO', 261 'Stopping services %s prior to migrating '\ 262 'data' % svc) 263 utils.stop(svc) 264 265 place_data_on_ceph(service, blk_device, mount_point, fstype) 266 267 for svc in system_services: 268 utils.start(svc)