#!/usr/bin/python import argparse import errno import json import logging import os import subprocess import sys import time LOG = logging.getLogger(os.path.basename(sys.argv[0])) QUORUM_STATES = ['leader', 'peon'] def wait_for_quorum(cluster, mon_id): while True: p = subprocess.Popen( args=[ 'ceph', '--cluster={cluster}'.format(cluster=cluster), '--admin-daemon=/var/run/ceph/{cluster}-mon.{mon_id}.asok'.format( cluster=cluster, mon_id=mon_id, ), 'mon_status', ], stdout=subprocess.PIPE, ) out = p.stdout.read() returncode = p.wait() if returncode != 0: LOG.info('ceph-mon admin socket not ready yet.') time.sleep(1) continue if out == '': LOG.info('ceph-mon admin socket returned no data') time.sleep(1) continue try: data = json.loads(out) except: LOG.info('failed to parse json %s', out) sys.exit(errno.EINVAL) state = data['state'] if state not in QUORUM_STATES: LOG.info('ceph-mon is not in quorum: %r', state) time.sleep(1) continue break def get_key(cluster, mon_id): path = '/etc/ceph/{cluster}.client.admin.keyring'.format( cluster=cluster, ) if os.path.exists(path): LOG.info('Key exists already: %s', path) return tmp = '{path}.{pid}.tmp'.format( path=path, pid=os.getpid(), ) pathdir = os.path.dirname(path) if not os.path.exists(pathdir): os.makedirs(pathdir) while True: try: with file(tmp, 'w') as f: os.fchmod(f.fileno(), 0600) LOG.info('Talking to monitor...') returncode = subprocess.call( args=[ 'ceph', '--cluster={cluster}'.format(cluster=cluster), '--name=mon.', '--keyring=/var/lib/ceph/mon/{cluster}-{mon_id}/keyring'.format( cluster=cluster, mon_id=mon_id, ), 'auth', 'get-or-create', 'client.admin', 'mon', 'allow *', 'osd', 'allow *', 'mds', 'allow', ], stdout=f, ) if returncode != 0: if returncode == errno.EPERM or returncode == errno.EACCES: LOG.info('Cannot get or create admin key, permission denied') sys.exit(returncode) else: LOG.info('Cannot get or create admin key') time.sleep(1) continue os.rename(tmp, path) break finally: try: os.unlink(tmp) except OSError as e: if e.errno == errno.ENOENT: pass else: raise def bootstrap_key(cluster, type_): path = '/var/lib/ceph/bootstrap-{type}/{cluster}.keyring'.format( type=type_, cluster=cluster, ) if os.path.exists(path): LOG.info('Key exists already: %s', path) return tmp = '{path}.{pid}.tmp'.format( path=path, pid=os.getpid(), ) args = [ 'ceph', '--cluster={cluster}'.format(cluster=cluster), 'auth', 'get-or-create', 'client.bootstrap-{type}'.format(type=type_), 'mon', 'allow profile bootstrap-{type}'.format(type=type_), ] pathdir = os.path.dirname(path) if not os.path.exists(pathdir): os.makedirs(pathdir) while True: try: with file(tmp, 'w') as f: os.fchmod(f.fileno(), 0600) LOG.info('Talking to monitor...') returncode = subprocess.call( args=args, stdout=f, ) if returncode != 0: if returncode == errno.EPERM or returncode == errno.EACCES: LOG.info('Cannot get or create bootstrap key for %s, permission denied', type_) break else: LOG.info('Cannot get or create bootstrap key for %s', type_) time.sleep(1) continue os.rename(tmp, path) break finally: try: os.unlink(tmp) except OSError as e: if e.errno == errno.ENOENT: pass else: raise def parse_args(): parser = argparse.ArgumentParser( description='Create Ceph client.admin key when ceph-mon is ready', ) parser.add_argument( '-v', '--verbose', action='store_true', default=None, help='be more verbose', ) parser.add_argument( '--cluster', metavar='NAME', help='name of the cluster', ) parser.add_argument( '--id', '-i', metavar='ID', help='id of a ceph-mon that is coming up', required=True, ) parser.set_defaults( cluster='ceph', ) parser.set_defaults( # we want to hold on to this, for later prog=parser.prog, ) args = parser.parse_args() return args def main(): args = parse_args() loglevel = logging.INFO if args.verbose: loglevel = logging.DEBUG logging.basicConfig( level=loglevel, ) wait_for_quorum(cluster=args.cluster, mon_id=args.id) get_key(cluster=args.cluster, mon_id=args.id) bootstrap_key( cluster=args.cluster, type_='osd', ) bootstrap_key( cluster=args.cluster, type_='mds', ) if __name__ == '__main__': main()