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)