]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
ceph-daemon: bootstrap and deploy (mgr) work
authorSage Weil <sage@redhat.com>
Thu, 26 Sep 2019 21:44:51 +0000 (16:44 -0500)
committerSage Weil <sage@redhat.com>
Wed, 2 Oct 2019 12:11:12 +0000 (07:11 -0500)
Signed-off-by: Sage Weil <sage@redhat.com>
src/ceph-daemon

index 0332d9bb1c83b8827ebe911da0247ef11fcd1a17..a8764d282e94be76118fe46e8f14e12a629e0b7f 100755 (executable)
@@ -5,12 +5,14 @@ CEPH_USER_GID=167
 DEFAULT_IMAGE='ceph/daemon-base'
 DATA_DIR='/var/lib/ceph'
 LOG_DIR='/var/log/ceph'
+UNIT_DIR='/etc/systemd/system'
 VERSION='unknown development version'
 
 import argparse
 import logging
 import os
 import sys
+import tempfile
 from distutils.spawn import find_executable
 from subprocess import check_output, CalledProcessError
 
@@ -36,19 +38,137 @@ def find_program(filename):
     return name
 
 def get_data_dir(base, fsid, t, n):
-    return base + '/' + fsid + '/' + t + '.' + n;
+    return base + '/' + fsid + '/' + t + '.' + n
 
-def get_daemon_args():
+def get_log_dir(base, fsid):
+    return base + '/' + fsid
+
+def get_daemon_args(daemon_type, daemon_id):
     r = [
+        '--default-admin-socket', '/var/run/ceph/' + args.fsid + '-' + daemon_type + '.' + daemon_id + '.asok',
+#        '--default-log-to-file=false',
 #        '--log-dir', args.log_dir,
 #        '--data-dir', args.data_dir,
         ]
-#    if args.uid != 0:
-#        r = r + ['--setuser', args.uid]
-#    if args.gid != 0:
-#        r = r + ['--setgroup', args.gid]
+    if args.uid != 0:
+        r = r + ['--setuser', str(args.uid)]
+    if args.gid != 0:
+        r = r + ['--setgroup', str(args.gid)]
     return r
 
+
+def create_daemon_dirs(fsid, daemon_type, daemon_id,
+                       config=None, keyring=None):
+    data_dir = get_data_dir(args.data_dir, fsid, daemon_type, daemon_id)
+    makedirs(data_dir)
+    if args.uid and args.gid:
+        os.chown(data_dir, args.uid, args.gid)
+    log_dir = get_log_dir(args.log_dir, fsid)
+    makedirs(log_dir)
+    if args.uid and args.gid:
+        os.chown(log_dir, args.uid, args.gid)
+
+    if config:
+        with open(data_dir + '/conf', 'w') as f:
+            f.write(config)
+            if args.uid and args.gid:
+                os.fchown(f.fileno(), args.uid, args.gid)
+    if keyring:
+        with open(data_dir + '/keyring', 'w') as f:
+            f.write(keyring)
+            os.fchmod(f.fileno(), 0o600)
+            if args.uid and args.gid:
+                os.fchown(f.fileno(), args.uid, args.gid)
+
+def get_config_and_keyring():
+    if args.conf_and_key:
+        import json
+        if args.conf_and_key == '-':
+            j = sys.stdin.read()
+        else:
+            with open(args.conf_and_key, 'r') as f:
+                j = f.read()
+        d = json.loads(j)
+        config = d.get('config')
+        keyring = '[%s]\n\tkey = %s\n' % (args.name, d.get('key'))
+    else:
+        if args.key:
+            keyring = '[%s]\n\tkey = %s\n' % (args.name, args.key)
+        elif args.keyring:
+            with open(args.keyring, 'r') as f:
+                keyring = f.read()
+        else:
+            raise RuntimeError('no keyring')
+        with open(args.conf, 'r') as f:
+            config = f.read()
+    return (config, keyring)
+
+def get_container(fsid, daemon_type, daemon_id):
+    data_dir = get_data_dir(args.data_dir, fsid, daemon_type, daemon_id)
+    log_dir = get_log_dir(args.log_dir, fsid)
+
+    cdata_dir = '/var/lib/ceph/%s/ceph-%s' % (daemon_type, daemon_id)
+    extra_args = []
+
+    return CephContainer(
+        image=args.image,
+        entrypoint='ceph-' + daemon_type,
+        args=['-i', daemon_id,
+              '-c', cdata_dir + '/conf',
+              '-f', # foreground
+        ] + extra_args + get_daemon_args(daemon_type, daemon_id),
+        volume_mounts={
+            log_dir: '/var/log/ceph:z',
+            data_dir: cdata_dir + ':z',
+        },
+        dname=daemon_type + '.' + daemon_id,
+        cname='ceph-%s-%s.%s' % (args.fsid, daemon_type, daemon_id),
+    )
+
+def deploy_daemon(daemon_type, daemon_id, c, config=None, keyring=None):
+    # dirs, conf, keyring
+    create_daemon_dirs(
+        args.fsid, daemon_type, daemon_id,
+        config, keyring)
+
+    # cmd
+    data_dir = get_data_dir(args.data_dir, args.fsid, daemon_type, daemon_id)
+    with open(data_dir + '/cmd', 'w') as f:
+        f.write('#!/bin/sh\n' + ' '.join(c.run_cmd()) + '\n')
+        os.fchmod(f.fileno(), 0o700)
+
+    # unit
+    unit = get_unit_file()
+    unit_file = 'ceph-%s@.service' % (args.fsid)
+    with open(args.unit_dir + '/' + unit_file, 'w') as f:
+        f.write(unit)
+    check_output(['systemctl', 'daemon-reload'])
+    unit_name = 'ceph-%s@%s.%s' % (args.fsid, daemon_type, daemon_id)
+    check_output(['systemctl', 'enable', unit_name])
+    check_output(['systemctl', 'start', unit_name])
+
+def get_unit_file():
+    u = """[Unit]
+Description=Ceph daemon for {fsid}
+After=network.target
+
+[Service]
+EnvironmentFile=-/etc/environment
+ExecStartPre=-/usr/bin/podman rm ceph-{fsid}-%i
+ExecStartPre=-mkdir -p /var/run/ceph
+ExecStart={data_dir}/{fsid}/%i/cmd
+ExecStop=-/usr/bin/podman stop ceph-{fsid}-%i
+ExecStopPost=-/bin/rm -f /var/run/ceph/{fsid}-%i.asok
+Restart=always
+RestartSec=10s
+TimeoutStartSec=120
+TimeoutStopSec=15
+
+[Install]
+WantedBy=multi-user.target
+""".format(fsid=args.fsid, data_dir=args.data_dir)
+    return u
+
 ##################################
 
 class CephContainer:
@@ -57,13 +177,20 @@ class CephContainer:
                  entrypoint,
                  args=[],
                  volume_mounts={},
-                 name='',
+                 cname='',
+                 dname='',
                  podman_args=[]):
         self.image = image
         self.entrypoint = entrypoint
         self.args = args
         self.volume_mounts = volume_mounts
-        self.name = name
+        self.cname = cname
+        self.dname = dname
+        if dname:
+            (self.daemon_type, self.daemon_id) = dname.split('.')
+        else:
+            self.daemon_type = None
+            self.daemon_id = None
         self.podman_args = podman_args
 
     def run_cmd(self):
@@ -74,13 +201,13 @@ class CephContainer:
             '-e', f'CONTAINER_IMAGE={self.image}',
             '-e', f'NODE_NAME={get_hostname()}',
         ]
-        name = ['--name', self.name] if self.name else []
+        cname = ['--name', self.cname] if self.cname else []
         return [
             find_program('podman'),
             'run',
             '--rm',
             '--net=host',
-        ] + self.podman_args + name + envs + vols + [
+        ] + self.podman_args + cname + envs + vols + [
             '--entrypoint', f'/usr/bin/{self.entrypoint}',
             self.image
         ] + self.args
@@ -104,26 +231,60 @@ def command_bootstrap():
     mon_id = args.mon_id or get_hostname()
     mgr_id = args.mgr_id or get_hostname()
     logging.debug('fsid %s, mon_id %s, mgr_id %s' % (fsid, mon_id, mgr_id))
-    mon_dir = get_data_dir(args.data_dir, fsid, 'mon', mon_id)
-    makedirs(mon_dir)
-    log_dir = get_data_dir(args.log_dir, fsid, 'mon', mon_id)
-    makedirs(log_dir)
 
-    mounts = {
-        log_dir: '/var/log/ceph:z',
-        mon_dir: '/var/lib/ceph/mon/ceph-' + mon_id + ':z',
-        '/tmp': '/mytmp:z',
-    }
+    # create some initial keys
+    mon_key = CephContainer(
+        image=args.image,
+        entrypoint='ceph-authtool',
+        args=['--gen-print-key'],
+    ).run().decode('utf-8').strip()
+    admin_key = CephContainer(
+        image=args.image,
+        entrypoint='ceph-authtool',
+        args=['--gen-print-key'],
+    ).run().decode('utf-8').strip()
+    mgr_key = CephContainer(
+        image=args.image,
+        entrypoint='ceph-authtool',
+        args=['--gen-print-key'],
+    ).run().decode('utf-8').strip()
 
-    # create initial monmap
+    keyring = ('[mon.]\n'
+               '\tkey = %s\n'
+               '\tcaps mon = allow *\n'
+               '[client.admin]\n'
+               '\tkey = %s\n'
+               '\tcaps mon = allow *\n'
+               '\tcaps mds = allow *\n'
+               '\tcaps mgr = allow *\n'
+               '\tcaps osd = allow *\n'
+               '[mgr.%s]\n'
+               '\tkey = %s\n'
+               '\tcaps mon = allow profile mgr\n'
+               '\tcaps mds = allow *\n'
+               '\tcaps osd = allow *\n'
+               % (mon_key, admin_key, mgr_id, mgr_key))
+
+    # tmp keyring file
+    tmp_keyring = tempfile.NamedTemporaryFile(mode='w')
+    os.fchmod(tmp_keyring.fileno(), 0o600)
+    if args.uid and args.gid:
+        os.fchown(tmp_keyring.fileno(), args.uid, args.gid)
+    tmp_keyring.write(keyring)
+    tmp_keyring.flush()
+
+    # config
     if args.mon_ip:
         addr_arg = '[v2:%s:3300,v1:%s:6789]' % (args.mon_ip, args.mon_ip)
     elif args.mon_addrv:
         addr_arg = args.mon_addrv
     else:
         raise RuntimeError('must specify --mon-ip or --mon-addrv')
-    cmonpath = '/mytmp/monmap'
-    ckeyring = '/mytmp/keyring'
+    config = '[global]\n\tfsid = %s\n\tmon host = %s\n' % (args.fsid, addr_arg)
+
+    # create initial monmap, tmp monmap file
+    tmp_monmap = tempfile.NamedTemporaryFile(mode='w')
+    os.fchmod(tmp_monmap.fileno(), 0o644)
     out = CephContainer(
         image=args.image,
         entrypoint='monmaptool',
@@ -131,60 +292,16 @@ def command_bootstrap():
               '--clobber',
               '--fsid', fsid,
               '--addv', mon_id, addr_arg,
-              cmonpath],
-        volume_mounts=mounts,
+              '/tmp/monmap'],
+        volume_mounts={
+            tmp_monmap.name: '/tmp/monmap:z',
+            },
     ).run()
 
-    # create initial keyring
-    mon_key = CephContainer(
-        image=args.image,
-        entrypoint='ceph-authtool',
-        args=['--gen-print-key'],
-        volume_mounts=mounts,
-    ).run().decode('utf-8').strip()
-    admin_key = CephContainer(
-        image=args.image,
-        entrypoint='ceph-authtool',
-        args=['--gen-print-key'],
-        volume_mounts=mounts,
-    ).run().decode('utf-8').strip()
-    with open('/tmp/keyring', 'w') as f:
-        f.write('[mon.]\n'
-                '\tkey = %s\n'
-                '\tcaps mon = *\n'
-                '[client.admin]\n'
-                '\tkey = %s\n'
-                '\tcaps mon = *\n'
-                '\tcaps mds = *\n'
-                '\tcaps mgr = *\n'
-                '\tcaps osd = *\n'
-            % (mon_key, admin_key));
-    """
-    CephContainer(
-        image=args.image,
-        entrypoint='ceph-authtool',
-        args=['--create-keyring',
-              '--gen-key',
-              '-n', 'mon.',
-              '--cap', 'mon', 'allow *',
-              ckeyring],
-        volume_mounts=mounts,
-    ).run()
-    CephContainer(
-        image=args.image,
-        entrypoint='ceph-authtool',
-        args=['--gen-key',
-              '-n', 'client.admin',
-              '--cap', 'mon', 'allow *',
-              '--cap', 'mgr', 'allow *',
-              '--cap', 'mds', 'allow *',
-              '--cap', 'osd', 'allow *',
-              ckeyring],
-        volume_mounts=mounts,
-    ).run()
-    """
-
-    # mkfs on the mon
+    # create mon
+    create_daemon_dirs(args.fsid, 'mon', mon_id)
+    mon_dir = get_data_dir(args.data_dir, fsid, 'mon', mon_id)
+    log_dir = get_log_dir(args.log_dir, fsid)
     out = CephContainer(
         image=args.image,
         entrypoint='ceph-mon',
@@ -192,18 +309,31 @@ def command_bootstrap():
               '-i', mon_id,
               '--fsid', fsid,
               '-c', '/dev/null',
-              '--monmap', cmonpath,
-              '--keyring', ckeyring,
+              '--monmap', '/tmp/monmap',
+              '--keyring', '/tmp/keyring',
               '--debug-mon', '20',
-              ] + get_daemon_args(),
-        volume_mounts=mounts,
+              ] + get_daemon_args('mon', mon_id),
+        volume_mounts={
+            log_dir: '/var/log/ceph:z',
+            mon_dir: '/var/lib/ceph/mon/ceph-%s:z' % (mon_id),
+            tmp_keyring.name: '/tmp/keyring:z',
+            tmp_monmap.name: '/tmp/monmap:z',
+        },
     ).run()
     print(out.decode('utf-8'))
 
+    with open(mon_dir + '/conf', 'w') as f:
+        f.write('[global]\n\tfsid = %s\n' % (args.fsid))
+
+    mon_c = get_container(args.fsid, 'mon', mon_id)
+    deploy_daemon('mon', mon_id, mon_c)
 
-    # create a mgr
+    # create mgr
+    mgr_keyring = '[mgr.%s]\n\tkey = %s\n' % (mgr_id, mgr_key)
+    mgr_c = get_container(args.fsid, 'mgr', mgr_id)
+    deploy_daemon('mgr', mgr_id, mgr_c, config, mgr_keyring)
 
-    # output
+    # output files
     if args.output_keyring:
         with open(args.output_keyring, 'w') as f:
             f.write('[client.admin]\n'
@@ -211,36 +341,27 @@ def command_bootstrap():
             os.fchmod(f.fileno(), 0o600)
     if args.output_conf:
         with open(args.output_conf, 'w') as f:
-            f.write('[global]\n'
-                    '\tmon host = ' + addr_arg + '\n')
+            f.write(config)
 
-    return 1
+    return 0
 
 ##################################
 
-def command_run():
-    fsid = args.fsid
-    name = args.name
-    (daemon_type, daemon_id) = name.split('.')
+def command_deploy():
+    (daemon_type, daemon_id) = args.name.split('.')
+    if daemon_type not in ['mon', 'mgr', 'mds', 'osd', 'rgw']:
+        raise RuntimeError('daemon type %s not recognized' % daemon_type)
+    (config, keyring) = get_config_and_keyring()
 
-    data_dir = get_data_dir(args.data_dir, fsid, daemon_type, daemon_id)
-    makedirs(data_dir)
-    log_dir = get_data_dir(args.log_dir, fsid, daemon_type, daemon_id)
-    makedirs(log_dir)
+    c = get_container(args.fsid, daemon_type, daemon_id)
+    deploy_daemon(daemon_type, daemon_id, c, config, keyring)
 
-    mounts = {
-        log_dir: '/var/log/ceph:z',
-        data_dir: '/var/lib/ceph/' + daemon_type + '/ceph-' + daemon_id + ':z',
-    }
-    out = CephContainer(
-        image=args.image,
-        entrypoint='ceph-' + daemon_type,
-        args=['-i', daemon_id,
-              '-c', '/dev/null',
-              '-f', '-d',
-        ] + get_daemon_args(),
-        volume_mounts=mounts,
-    ).run()
+##################################
+
+def command_run():
+    (daemon_type, daemon_id) = args.name.split('.')
+    c = get_container(args.fsid, daemon_type, daemon_id)
+    c.run()
 
 
 ##################################
@@ -278,6 +399,10 @@ parser.add_argument(
     '--log-dir',
     default=LOG_DIR,
     help='base directory for daemon logs')
+parser.add_argument(
+    '--unit-dir',
+    default=UNIT_DIR,
+    help='base directory for systemd units')
 subparsers = parser.add_subparsers(help='sub-command')
 
 parser_version = subparsers.add_parser(
@@ -327,6 +452,30 @@ parser_bootstrap.add_argument(
     '--output-conf',
     help='location to write conf file to connect to new cluster')
 
+parser_deploy = subparsers.add_parser(
+    'deploy', help='deploy a daemon')
+parser_deploy.set_defaults(func=command_deploy)
+parser_deploy.add_argument(
+    '--name',
+    required=True,
+    help='daemon name (type.id)')
+parser_deploy.add_argument(
+    '--fsid',
+    required=True,
+    help='cluster FSID')
+parser_deploy.add_argument(
+    '--conf',
+    help='config file for new daemon')
+parser_deploy.add_argument(
+    '--keyring',
+    help='keyring for new daemon')
+parser_deploy.add_argument(
+    '--key',
+    help='key for new daemon')
+parser_deploy.add_argument(
+    '--conf-and-key',
+    help='JSON file with config and key')
+
 args = parser.parse_args()
 
 if 'func' not in args: