--- /dev/null
+#!/usr/bin/env python
+import argparse
+import errno
+import json
+import logging
+import os
+import subprocess
+import sys
+import time
+import pwd
+import grp
+
+
+LOG = logging.getLogger(os.path.basename(sys.argv[0]))
+
+QUORUM_STATES = ['leader', 'peon']
+
+def get_ceph_uid():
+ try:
+ uid = pwd.getpwnam('ceph').pw_uid
+ except:
+ uid = -1
+ return uid
+
+def get_ceph_gid():
+ try:
+ gid = grp.getgrnam('ceph').gr_gid
+ except:
+ gid = -1
+ return gid
+
+def wait_for_quorum(cluster, mon_id, wait_count=600):
+ # wait 10 minutes by default
+ while wait_count > 0:
+ 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)
+ wait_count -= 1
+ continue
+
+ if out == '':
+ LOG.info('ceph-mon admin socket returned no data')
+ time.sleep(1)
+ wait_count -= 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)
+ wait_count -= 1
+ continue
+
+ break
+
+ if wait_count == 0:
+ raise SystemExit("ceph-mon was not able to join quorum within %d seconds" % wait_count)
+
+
+def get_key(cluster, mon_id, wait_count=600):
+ 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)
+ os.chmod(pathdir, 0770)
+ os.chown(pathdir, get_ceph_uid(), get_ceph_gid())
+ while wait_count > 0:
+ try:
+ with file(tmp, 'w') as f:
+ os.fchmod(f.fileno(), 0600)
+ os.fchown(f.fileno(), get_ceph_uid(), get_ceph_gid())
+ LOG.info('Talking to monitor...')
+
+ args_prefix = [
+ "ceph",
+ '--connect-timeout=20',
+ '--cluster={cluster}'.format(cluster=cluster),
+ '--name=mon.',
+ '--keyring=/var/lib/ceph/mon/{cluster}-{mon_id}/keyring'.format(
+ cluster=cluster,
+ mon_id=mon_id,
+ ),
+ ]
+
+ # First try getting the key if it already exists, to handle
+ # the case where it exists but doesn't match the caps
+ # we would pass into get-or-create.
+ returncode = subprocess.call(
+ args=args_prefix + [
+ 'auth',
+ 'get',
+ 'client.admin',
+ ],
+ stdout=f,
+ )
+ if returncode == errno.ENOENT:
+ returncode = subprocess.call(
+ args=args_prefix + [
+ 'auth',
+ 'get-or-create',
+ 'client.admin',
+ 'mon', 'allow *',
+ 'osd', 'allow *',
+ 'mds', 'allow *',
+ 'mgr', 'allow *',
+ ],
+ stdout=f,
+ )
+ else:
+ returncode = subprocess.call(
+ args=args_prefix + [
+ 'auth',
+ 'caps',
+ 'client.admin',
+ 'mon', 'allow *',
+ 'osd', 'allow *',
+ 'mds', 'allow *',
+ 'mgr', '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)
+ wait_count -= 1
+ continue
+
+ os.rename(tmp, path)
+ break
+ finally:
+ try:
+ os.unlink(tmp)
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ pass
+ else:
+ raise
+
+ if wait_count == 0:
+ raise SystemExit("Could not get or create the admin key after %d seconds" % wait_count)
+
+
+def bootstrap_key(cluster, type_, wait_count=600):
+ 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',
+ '--connect-timeout=20',
+ '--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)
+ os.chmod(pathdir, 0770)
+ os.chown(pathdir, get_ceph_uid(), get_ceph_gid())
+
+ while wait_count > 0:
+ try:
+ with file(tmp, 'w') as f:
+ os.fchmod(f.fileno(), 0600)
+ os.fchown(f.fileno(), get_ceph_uid(), get_ceph_gid())
+ 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)
+ wait_count -= 1
+ continue
+
+ os.rename(tmp, path)
+ break
+ finally:
+ try:
+ os.unlink(tmp)
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ pass
+ else:
+ raise
+ if wait_count == 0:
+ raise SystemExit("Could not get or create %s bootstrap key after %d seconds" % (type_, wait_count))
+
+
+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.add_argument(
+ '--timeout', '-t',
+ metavar='TIMEOUT',
+ type=int,
+ help='timeout in seconds to wait',
+ )
+ parser.set_defaults(
+ cluster='ceph',
+ timeout=600,
+ )
+ 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, wait_count=args.timeout)
+ get_key(cluster=args.cluster, mon_id=args.id, wait_count=args.timeout)
+
+ bootstrap_key(
+ cluster=args.cluster,
+ type_='osd',
+ wait_count=args.timeout,
+ )
+ bootstrap_key(
+ cluster=args.cluster,
+ type_='rgw',
+ wait_count=args.timeout,
+ )
+ bootstrap_key(
+ cluster=args.cluster,
+ type_='mds',
+ wait_count=args.timeout,
+ )
+ bootstrap_key(
+ cluster=args.cluster,
+ type_='rbd',
+ wait_count=args.timeout,
+ )
+
+if __name__ == '__main__':
+ main()