]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ansible.git/commitdiff
ceph_key: add fetch_initial_keys capability
authorSébastien Han <seb@redhat.com>
Tue, 23 Oct 2018 16:38:40 +0000 (18:38 +0200)
committermergify[bot] <mergify[bot]@users.noreply.github.com>
Thu, 8 Nov 2018 13:32:18 +0000 (13:32 +0000)
This is needed for Nautilus since the ceph-create-keys script goes away.
(https://github.com/ceph/ceph/pull/21305)
Now the module if called with 'state: fetch_initial_keys' will lookup
keys generated by the monitor and write them down on the filesystem to
the right location (/etc/ceph and /var/lib/ceph/boostrap*).
This is not applicable to container since keys are generated by the
container only.

Signed-off-by: Sébastien Han <seb@redhat.com>
infrastructure-playbooks/ceph-keys.yml
library/ceph_key.py
library/test_ceph_key.py
roles/ceph-config/tasks/create_ceph_initial_dirs.yml
roles/ceph-mon/tasks/ceph_keys.yml

index 82da2eb0abb2ce8d5f87b36673802984792d9e2b..08bdda89e2d3db251ce5969cd41a891a908b13b0 100644 (file)
@@ -66,3 +66,9 @@
         containerized: "{{ docker_exec_cmd | default(False) }}"
       register: list_keys
       ignore_errors: true
+
+    - name: fetch_initial_keys
+      ceph_key:
+        state: fetch_initial_keys
+        cluster: "{{ cluster }}"
+      ignore_errors: true
\ No newline at end of file
index e6d66bac16d7f45ab379ebc18dcdffec08a5d480..872b6361c8b288d49b5903171538123769946db1 100644 (file)
@@ -97,13 +97,21 @@ options:
             - Destination to write the keyring
         required: false
         default: /etc/ceph/
+    fetch_initial_keys:
+        description:
+            - Fetch client.admin and bootstrap key.
+            This is only needed for Nautilus and above.
+            Writes down to the filesystem the initial keys generated by the monitor.  # noqa E501
+            This command can ONLY run from a monitor node.
+        required: false
+        default: false
 '''
 
 EXAMPLES = '''
 
 keys_to_create:
-  - { name: client.key, key: "AQAin8tUUK84ExAA/QgBtI7gEMWdmnvKBzlXdQ==", caps: { mon: "allow rwx", mds: "allow *" } , mode: "0600" }
-  - { name: client.cle, caps: { mon: "allow r", osd: "allow *" } , mode: "0600" }
+  - { name: client.key, key: "AQAin8tUUK84ExAA/QgBtI7gEMWdmnvKBzlXdQ==", caps: { mon: "allow rwx", mds: "allow *" } , mode: "0600" } # noqa e501
+  - { name: client.cle, caps: { mon: "allow r", osd: "allow *" } , mode: "0600" } # noqa e501
 
 caps:
   mon: "allow rwx"
@@ -165,16 +173,25 @@ caps:
 - name: list cephx keys
   ceph_key:
     state: list
+
+- name: fetch cephx keys
+  ceph_key:
+    state: fetch_initial_keys
 '''
 
 RETURN = '''#  '''
 
-from ansible.module_utils.basic import AnsibleModule
-import datetime
-import os
-import struct
-import time
-import base64
+from ansible.module_utils.basic import AnsibleModule  # noqa E402
+import datetime  # noqa E402
+import grp  # noqa E402
+import json  # noqa E402
+import os  # noqa E402
+import pwd  # noqa E402
+import stat  # noqa E402
+import struct  # noqa E402
+import time  # noqa E402
+import base64  # noqa E402
+import socket  # noqa E402
 
 
 def fatal(message, module):
@@ -217,7 +234,7 @@ def generate_caps(cmd, _type, caps):
     return cmd
 
 
-def generate_ceph_cmd(cluster, args, containerized=None):
+def generate_ceph_cmd(cluster, args, user, user_key, containerized=None):
     '''
     Generate 'ceph' command line to execute
     '''
@@ -226,6 +243,10 @@ def generate_ceph_cmd(cluster, args, containerized=None):
 
     base_cmd = [
         'ceph',
+        '-n',
+        user,
+        '-k',
+        user_key,
         '--cluster',
         cluster,
         'auth',
@@ -239,7 +260,7 @@ def generate_ceph_cmd(cluster, args, containerized=None):
     return cmd
 
 
-def generate_ceph_authtool_cmd(cluster, name, secret, caps, auid, dest, containerized=None):
+def generate_ceph_authtool_cmd(cluster, name, secret, caps, auid, dest, containerized=None):  # noqa E501
     '''
     Generate 'ceph-authtool' command line to execute
     '''
@@ -268,7 +289,7 @@ def generate_ceph_authtool_cmd(cluster, name, secret, caps, auid, dest, containe
     return cmd
 
 
-def create_key(module, result, cluster, name, secret, caps, import_key, auid, dest, containerized=None):
+def create_key(module, result, cluster, name, secret, caps, import_key, auid, dest, containerized=None):  # noqa E501
     '''
     Create a CephX key
     '''
@@ -289,7 +310,12 @@ def create_key(module, result, cluster, name, secret, caps, import_key, auid, de
         cluster, name, secret, caps, auid, dest, containerized))
 
     if import_key:
-        cmd_list.append(generate_ceph_cmd(cluster, args, containerized))
+        user = "client.admin"
+        user = "client.admin"
+        user_key = os.path.join(
+            "/etc/ceph/" + cluster + ".client.admin.keyring")
+        cmd_list.append(generate_ceph_cmd(
+            cluster, args, user, user_key, containerized))
 
     return cmd_list
 
@@ -307,7 +333,11 @@ def update_key(cluster, name, caps, containerized=None):
     ]
 
     args = generate_caps(args, "ceph", caps)
-    cmd_list.append(generate_ceph_cmd(cluster, args, containerized))
+    user = "client.admin"
+    user_key = os.path.join(
+        "/etc/ceph/" + cluster + ".client.admin.keyring")
+    cmd_list.append(generate_ceph_cmd(
+        cluster, args, user, user_key, containerized))
 
     return cmd_list
 
@@ -324,12 +354,16 @@ def delete_key(cluster, name, containerized=None):
         name,
     ]
 
-    cmd_list.append(generate_ceph_cmd(cluster, args, containerized))
+    user = "client.admin"
+    user_key = os.path.join(
+        "/etc/ceph/" + cluster + ".client.admin.keyring")
+    cmd_list.append(generate_ceph_cmd(
+        cluster, args, user, user_key, containerized))
 
     return cmd_list
 
 
-def info_key(cluster, name, containerized=None):
+def info_key(cluster, name, user, user_key, output_format, containerized=None):
     '''
     Get information about a CephX key
     '''
@@ -340,15 +374,16 @@ def info_key(cluster, name, containerized=None):
         'get',
         name,
         '-f',
-        'json',
+        output_format,
     ]
 
-    cmd_list.append(generate_ceph_cmd(cluster, args, containerized))
+    cmd_list.append(generate_ceph_cmd(
+        cluster, args, user, user_key, containerized))
 
     return cmd_list
 
 
-def list_keys(cluster, containerized=None):
+def list_keys(cluster, user, user_key, containerized=None):
     '''
     List all CephX keys
     '''
@@ -361,7 +396,8 @@ def list_keys(cluster, containerized=None):
         'json',
     ]
 
-    cmd_list.append(generate_ceph_cmd(cluster, args, containerized))
+    cmd_list.append(generate_ceph_cmd(
+        cluster, args, user, user_key, containerized))
 
     return cmd_list
 
@@ -379,6 +415,53 @@ def exec_commands(module, cmd_list):
     return rc, cmd, out, err
 
 
+def lookup_ceph_initial_entities(out):
+    '''
+    Lookup Ceph initial keys entries in the auth map
+    '''
+
+    # convert out to json, ansible returns a string...
+    try:
+        out_dict = json.loads(out)
+    except ValueError as e:
+        fatal("Could not decode 'ceph auth list' json output: {}".format(e), module)  # noqa E501
+
+    entities = []
+    if "auth_dump" in out_dict:
+        for key in out_dict["auth_dump"]:
+            for k, v in key.items():
+                if k == "entity":
+                    if "client." in v:
+                        entities.append(v)
+    else:
+        fatal("'auth_dump' key not present in json output:", module)  # noqa E501
+
+    return entities
+
+
+def build_key_path(cluster, entity):
+    '''
+    Build key path depending on the key type
+    '''
+
+    if "admin" in entity:
+        path = "/etc/ceph"
+        key_path = os.path.join(
+            path + "/" + cluster + "." + entity + ".keyring")
+    elif "bootstrap" in entity:
+        path = "/var/lib/ceph"
+        # bootstrap keys show up as 'client.boostrap-osd'
+        # however the directory is called '/var/lib/ceph/bootstrap-osd'
+        # so we need to substring 'client.'
+        entity_split = entity.split('.')[1]
+        key_path = os.path.join(
+            path + "/" + entity_split + "/" + cluster + ".keyring")
+    else:
+        return None
+
+    return key_path
+
+
 def run_module():
     module_args = dict(
         cluster=dict(type='str', required=False, default='ceph'),
@@ -428,25 +511,29 @@ def run_module():
     # We only want to run this check when a key needs to be added
     # There is no guarantee that any cluster is running and we don't need one
     if import_key:
+        user = "client.admin"
+        user_key = os.path.join(
+            "/etc/ceph/" + cluster + ".client.admin.keyring")
+        output_format = "json"
         rc, cmd, out, err = exec_commands(
-            module, info_key(cluster, name, containerized))
+            module, info_key(cluster, name, user, user_key, output_format, containerized))  # noqa E501
 
     if state == "present":
         if not caps:
-            fatal("Capabilities must be provided when state is 'present'", module)
+            fatal("Capabilities must be provided when state is 'present'", module)  # noqa E501
 
         # We allow 'present' to override any existing key
         # ONLY if a secret is provided
         # if not we skip the creation
         if import_key:
             if rc == 0 and not secret:
-                result["stdout"] = "skipped, since {0} already exists, if you want to update a key use 'state: update'".format(
+                result["stdout"] = "skipped, since {0} already exists, if you want to update a key use 'state: update'".format(  # noqa E501
                     name)
                 result['rc'] = rc
                 module.exit_json(**result)
 
         rc, cmd, out, err = exec_commands(module, create_key(
-            module, result, cluster, name, secret, caps, import_key, auid, dest, containerized))
+            module, result, cluster, name, secret, caps, import_key, auid, dest, containerized))  # noqa E501
 
         file_path = os.path.join(
             dest + "/" + cluster + "." + name + ".keyring")
@@ -455,7 +542,7 @@ def run_module():
         module.set_fs_attributes_if_different(file_args, False)
     elif state == "update":
         if not caps:
-            fatal("Capabilities must be provided when state is 'update'", module)
+            fatal("Capabilities must be provided when state is 'update'", module)  # noqa E501
 
         if rc != 0:
             result["stdout"] = "skipped, since {0} does not exist".format(name)
@@ -475,16 +562,71 @@ def run_module():
             result['rc'] = 0
             module.exit_json(**result)
 
+        user = "client.admin"
+        user_key = os.path.join(
+            "/etc/ceph/" + cluster + ".client.admin.keyring")
+        output_format = "json"
         rc, cmd, out, err = exec_commands(
-            module, info_key(cluster, name, containerized))
+            module, info_key(cluster, name, user, user_key, output_format, containerized))  # noqa E501
 
     elif state == "list":
+        user = "client.admin"
+        user_key = os.path.join(
+            "/etc/ceph/" + cluster + ".client.admin.keyring")
         rc, cmd, out, err = exec_commands(
-            module, list_keys(cluster, containerized))
+            module, list_keys(cluster, user, user_key, containerized))
+
+    elif state == "fetch_initial_keys":
+        hostname = socket.gethostname()
+        user = "mon."
+        user_key = os.path.join(
+            "/var/lib/ceph/mon/" + cluster + "-" + hostname + "/keyring")
+        rc, cmd, out, err = exec_commands(
+            module, list_keys(cluster, user, user_key, containerized))
+        if rc != 0:
+            result["stdout"] = "failed to retrieve ceph keys".format(name)
+            result['rc'] = 0
+            module.exit_json(**result)
+
+        entities = lookup_ceph_initial_entities(out)
+
+        # get ceph's group and user id
+        ceph_uid = pwd.getpwnam('ceph').pw_uid
+        ceph_grp = grp.getgrnam('ceph').gr_gid
+
+        output_format = "plain"
+        for entity in entities:
+            key_path = build_key_path(cluster, entity)
+            if key_path is None:
+                fatal("Failed to build key path, no entity yet?", module)
+            elif os.path.isfile(key_path):
+                # if the key is already on the filesystem
+                # there is no need to fetch it again
+                continue
+
+            extra_args = [
+                '-o',
+                key_path,
+            ]
+
+            info_cmd = info_key(cluster, entity, user,
+                                user_key, output_format, containerized)
+            # we use info_cmd[0] because info_cmd is an array made of an array
+            info_cmd[0].extend(extra_args)
+            rc, cmd, out, err = exec_commands(
+                module, info_cmd)  # noqa E501
+
+            # apply ceph:ceph ownership and mode 0400 on keys
+            try:
+                os.chown(key_path, ceph_uid, ceph_grp)
+                os.chmod(key_path, stat.S_IRUSR)
+            except OSError as e:
+                fatal("Failed to set owner/group/permissions of %s: %s" % (
+                    key_path, str(e)), module)
 
     else:
         module.fail_json(
-            msg='State must either be "present" or "absent" or "update" or "list" or "info".', changed=False, rc=1)
+            msg='State must either be "present" or "absent" or "update" or "list" or "info" or "fetch_initial_keys".', changed=False, rc=1)  # noqa E501
 
     endd = datetime.datetime.now()
     delta = endd - startd
index b443489ea6207254c66cc72c0b65cac5395881e7..113c1d5d445568dd598aeada7c4c9d0c13a269c0 100644 (file)
@@ -1,6 +1,7 @@
+import json
 import os
-import pytest
 from . import ceph_key
+from ansible.compat.tests.mock import MagicMock
 
 
 class TestCephKeyModule(object):
@@ -49,19 +50,28 @@ class TestCephKeyModule(object):
     def test_generate_ceph_cmd_list_non_container(self):
         fake_cluster = "fake"
         fake_args = ['arg']
+        fake_user = "fake-user"
+        fake_key = "/tmp/my-key"
         expected_command_list = [
             'ceph',
+            '-n',
+            "fake-user",
+            '-k',
+            "/tmp/my-key",
             '--cluster',
             fake_cluster,
             'auth',
             'arg'
         ]
-        result = ceph_key.generate_ceph_cmd(fake_cluster, fake_args)
+        result = ceph_key.generate_ceph_cmd(
+            fake_cluster, fake_args, fake_user, fake_key)
         assert result == expected_command_list
 
     def test_generate_ceph_cmd_list_container(self):
         fake_cluster = "fake"
         fake_args = ['arg']
+        fake_user = "fake-user"
+        fake_key = "/tmp/my-key"
         fake_containerized = "docker exec -ti ceph-mon"
         expected_command_list = [
             'docker',
@@ -69,13 +79,17 @@ class TestCephKeyModule(object):
             '-ti',
             'ceph-mon',
             'ceph',
+            '-n',
+            "fake-user",
+            '-k',
+            "/tmp/my-key",
             '--cluster',
             fake_cluster,
             'auth',
             'arg'
         ]
         result = ceph_key.generate_ceph_cmd(
-            fake_cluster, fake_args, fake_containerized)
+            fake_cluster, fake_args, fake_user, fake_key, fake_containerized)
         assert result == expected_command_list
 
     def test_generate_ceph_authtool_cmd_non_container_no_auid(self):
@@ -88,7 +102,7 @@ class TestCephKeyModule(object):
         }
         fake_dest = "/fake/ceph"
         fake_file_destination = os.path.join(
-            fake_dest + "/"+ fake_cluster + "." + fake_name + ".keyring")
+            fake_dest + "/" + fake_cluster + "." + fake_name + ".keyring")
         fake_auid = None
         expected_command_list = [
             'ceph-authtool',
@@ -106,7 +120,7 @@ class TestCephKeyModule(object):
             'allow rwx',
         ]
         result = ceph_key.generate_ceph_authtool_cmd(
-            fake_cluster, fake_name, fake_secret, fake_caps, fake_auid, fake_dest)
+            fake_cluster, fake_name, fake_secret, fake_caps, fake_auid, fake_dest)  # noqa E501
         assert result == expected_command_list
 
     def test_generate_ceph_authtool_cmd_non_container_auid(self):
@@ -139,7 +153,7 @@ class TestCephKeyModule(object):
             'allow rwx',
         ]
         result = ceph_key.generate_ceph_authtool_cmd(
-            fake_cluster, fake_name, fake_secret, fake_caps, fake_auid, fake_dest)
+            fake_cluster, fake_name, fake_secret, fake_caps, fake_auid, fake_dest)  # noqa E501
         assert result == expected_command_list
 
     def test_generate_ceph_authtool_cmd_container(self):
@@ -175,7 +189,7 @@ class TestCephKeyModule(object):
             'allow rwx'
         ]
         result = ceph_key.generate_ceph_authtool_cmd(
-            fake_cluster, fake_name, fake_secret, fake_caps, fake_auid, fake_dest, fake_containerized)
+            fake_cluster, fake_name, fake_secret, fake_caps, fake_auid, fake_dest, fake_containerized)  # noqa E501
         assert result == expected_command_list
 
     def test_create_key_non_container(self):
@@ -194,13 +208,13 @@ class TestCephKeyModule(object):
         fake_file_destination = os.path.join(
             fake_dest + "/" + fake_cluster + "." + fake_name + ".keyring")
         expected_command_list = [
-            ['ceph-authtool', '--create-keyring', fake_file_destination, '--name', fake_name,
-                '--add-key', fake_secret, '--cap', 'mon', 'allow *', '--cap', 'osd', 'allow rwx'],
-            ['ceph', '--cluster', fake_cluster, 'auth',
+            ['ceph-authtool', '--create-keyring', fake_file_destination, '--name', fake_name,  # noqa E501
+                '--add-key', fake_secret, '--cap', 'mon', 'allow *', '--cap', 'osd', 'allow rwx'],  # noqa E501
+            ['ceph', '-n', 'client.admin', '-k', '/etc/ceph/fake.client.admin.keyring', '--cluster', fake_cluster, 'auth',  # noqa E501
                 'import', '-i', fake_file_destination],
         ]
         result = ceph_key.create_key(fake_module, fake_result, fake_cluster,
-                                     fake_name, fake_secret, fake_caps, fake_import_key, fake_auid, fake_dest)
+                                     fake_name, fake_secret, fake_caps, fake_import_key, fake_auid, fake_dest)  # noqa E501
         assert result == expected_command_list
 
     def test_create_key_container(self):
@@ -220,13 +234,13 @@ class TestCephKeyModule(object):
         fake_file_destination = os.path.join(
             fake_dest + "/" + fake_cluster + "." + fake_name + ".keyring")
         expected_command_list = [
-            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph-authtool', '--create-keyring', fake_file_destination,
-                '--name', fake_name, '--add-key', fake_secret, '--cap', 'mon', 'allow *', '--cap', 'osd', 'allow rwx'],
-            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph', '--cluster',
+            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph-authtool', '--create-keyring', fake_file_destination,  # noqa E501
+                '--name', fake_name, '--add-key', fake_secret, '--cap', 'mon', 'allow *', '--cap', 'osd', 'allow rwx'],  # noqa E501
+            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph', '-n', 'client.admin', '-k', '/etc/ceph/fake.client.admin.keyring', '--cluster',  # noqa E501
                 fake_cluster, 'auth', 'import', '-i', fake_file_destination],
         ]
-        result = ceph_key.create_key(fake_module, fake_result, fake_cluster, fake_name,
-                                     fake_secret, fake_caps, fake_import_key, fake_auid, fake_dest, fake_containerized)
+        result = ceph_key.create_key(fake_module, fake_result, fake_cluster, fake_name,  # noqa E501
+                                     fake_secret, fake_caps, fake_import_key, fake_auid, fake_dest, fake_containerized)  # noqa E501
         assert result == expected_command_list
 
     def test_create_key_non_container_no_import(self):
@@ -244,7 +258,7 @@ class TestCephKeyModule(object):
         fake_auid = None
         fake_file_destination = os.path.join(
             fake_dest + "/" + fake_cluster + "." + fake_name + ".keyring")
-        # create_key passes (one for ceph-authtool and one for itself) itw own array so the expected result is an array within an array
+        # create_key passes (one for ceph-authtool and one for itself) itw own array so the expected result is an array within an array # noqa E501
         expected_command_list = [[
             'ceph-authtool',
             '--create-keyring',
@@ -261,7 +275,7 @@ class TestCephKeyModule(object):
             'allow rwx', ]
         ]
         result = ceph_key.create_key(fake_module, fake_result, fake_cluster,
-                                     fake_name, fake_secret, fake_caps, fake_import_key, fake_auid, fake_dest)
+                                     fake_name, fake_secret, fake_caps, fake_import_key, fake_auid, fake_dest)  # noqa E501
         assert result == expected_command_list
 
     def test_create_key_container_no_import(self):
@@ -280,7 +294,7 @@ class TestCephKeyModule(object):
         fake_file_destination = os.path.join(
             fake_dest + "/" + fake_cluster + "." + fake_name + ".keyring")
         fake_auid = None
-        # create_key passes (one for ceph-authtool and one for itself) itw own array so the expected result is an array within an array
+        # create_key passes (one for ceph-authtool and one for itself) itw own array so the expected result is an array within an array # noqa E501
         expected_command_list = [[
             'docker',
             'exec',
@@ -300,8 +314,8 @@ class TestCephKeyModule(object):
             'osd',
             'allow rwx', ]
         ]
-        result = ceph_key.create_key(fake_module, fake_result, fake_cluster, fake_name,
-                                     fake_secret, fake_caps, fake_import_key, fake_auid, fake_dest, fake_containerized)
+        result = ceph_key.create_key(fake_module, fake_result, fake_cluster, fake_name,  # noqa E501
+                                     fake_secret, fake_caps, fake_import_key, fake_auid, fake_dest, fake_containerized)  # noqa E501
         assert result == expected_command_list
 
     def test_update_key_non_container(self):
@@ -312,7 +326,7 @@ class TestCephKeyModule(object):
             'osd': 'allow rwx',
         }
         expected_command_list = [
-            ['ceph', '--cluster', fake_cluster, 'auth', 'caps',
+            ['ceph', '-n', 'client.admin', '-k', '/etc/ceph/fake.client.admin.keyring',  '--cluster', fake_cluster, 'auth', 'caps',  # noqa E501
                 fake_name, 'mon', 'allow *', 'osd', 'allow rwx'],
         ]
         result = ceph_key.update_key(fake_cluster, fake_name, fake_caps)
@@ -327,8 +341,8 @@ class TestCephKeyModule(object):
             'osd': 'allow rwx',
         }
         expected_command_list = [
-            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph', '--cluster', fake_cluster,
-                'auth', 'caps', fake_name, 'mon', 'allow *', 'osd', 'allow rwx'],
+            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph', '-n', 'client.admin', '-k', '/etc/ceph/fake.client.admin.keyring',  '--cluster', fake_cluster,  # noqa E501
+                'auth', 'caps', fake_name, 'mon', 'allow *', 'osd', 'allow rwx'],  # noqa E501
         ]
         result = ceph_key.update_key(
             fake_cluster, fake_name, fake_caps, fake_containerized)
@@ -338,7 +352,8 @@ class TestCephKeyModule(object):
         fake_cluster = "fake"
         fake_name = "client.fake"
         expected_command_list = [
-            ['ceph', '--cluster', fake_cluster, 'auth', 'del', fake_name],
+            ['ceph',  '-n', 'client.admin', '-k', '/etc/ceph/fake.client.admin.keyring',  # noqa E501
+                '--cluster', fake_cluster, 'auth', 'del', fake_name],
         ]
         result = ceph_key.delete_key(fake_cluster, fake_name)
         assert result == expected_command_list
@@ -348,7 +363,7 @@ class TestCephKeyModule(object):
         fake_name = "client.fake"
         fake_containerized = "docker exec -ti ceph-mon"
         expected_command_list = [
-            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph',
+            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph', '-n', 'client.admin', '-k', '/etc/ceph/fake.client.admin.keyring',  # noqa E501
                 '--cluster', fake_cluster, 'auth', 'del', fake_name],
         ]
         result = ceph_key.delete_key(
@@ -358,38 +373,100 @@ class TestCephKeyModule(object):
     def test_info_key_non_container(self):
         fake_cluster = "fake"
         fake_name = "client.fake"
+        fake_user = "fake-user"
+        fake_key = "/tmp/my-key"
+        fake_output_format = "json"
         expected_command_list = [
-            ['ceph', '--cluster', fake_cluster, 'auth',
+            ['ceph', '-n', "fake-user", '-k', "/tmp/my-key", '--cluster', fake_cluster, 'auth',  # noqa E501
                 'get', fake_name, '-f', 'json'],
         ]
-        result = ceph_key.info_key(fake_cluster, fake_name)
+        result = ceph_key.info_key(
+            fake_cluster, fake_name, fake_user, fake_key, fake_output_format)
         assert result == expected_command_list
 
     def test_info_key_container(self):
         fake_cluster = "fake"
         fake_name = "client.fake"
+        fake_user = "fake-user"
+        fake_key = "/tmp/my-key"
         fake_containerized = "docker exec -ti ceph-mon"
+        fake_output_format = "json"
         expected_command_list = [
-            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph', '--cluster',
+            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph', '-n', "fake-user", '-k', "/tmp/my-key", '--cluster',  # noqa E501
                 fake_cluster, 'auth', 'get', fake_name, '-f', 'json'],
         ]
-        result = ceph_key.info_key(fake_cluster, fake_name, fake_containerized)
+        result = ceph_key.info_key(
+            fake_cluster, fake_name, fake_user, fake_key, fake_output_format, fake_containerized)  # noqa E501
         assert result == expected_command_list
 
     def test_list_key_non_container(self):
         fake_cluster = "fake"
+        fake_user = "fake-user"
+        fake_key = "/tmp/my-key"
+        expected_command_list = [
+            ['ceph', '-n', "fake-user", '-k', "/tmp/my-key",
+                '--cluster', fake_cluster, 'auth', 'ls', '-f', 'json'],
+        ]
+        result = ceph_key.list_keys(fake_cluster, fake_user, fake_key)
+        assert result == expected_command_list
+
+    def test_list_key_non_container_with_mon_key(self):
+        fake_hostname = "mon01"
+        fake_cluster = "fake"
+        fake_user = "mon."
+        fake_key = os.path.join("/var/lib/ceph/mon/" + fake_cluster + "-" + fake_hostname + "/keyring") # noqa E501
+        expected_command_list = [
+            ['ceph', '-n', "mon.", '-k', "/var/lib/ceph/mon/fake-mon01/keyring",  # noqa E501
+                '--cluster', fake_cluster, 'auth', 'ls', '-f', 'json'],
+        ]
+        result = ceph_key.list_keys(fake_cluster, fake_user, fake_key)
+        assert result == expected_command_list
+
+    def test_list_key_container_with_mon_key(self):
+        fake_hostname = "mon01"
+        fake_cluster = "fake"
+        fake_containerized = "docker exec -ti ceph-mon"
+        fake_user = "mon."
+        fake_key = os.path.join("/var/lib/ceph/mon/" + fake_cluster + "-" + fake_hostname + "/keyring") # noqa E501
         expected_command_list = [
-            ['ceph', '--cluster', fake_cluster, 'auth', 'ls', '-f', 'json'],
+            ['docker', 'exec', '-ti', 'ceph-mon','ceph', '-n', "mon.", '-k', "/var/lib/ceph/mon/fake-mon01/keyring",  # noqa E501
+                '--cluster', fake_cluster, 'auth', 'ls', '-f', 'json'],
         ]
-        result = ceph_key.list_keys(fake_cluster)
+        result = ceph_key.list_keys(fake_cluster, fake_user, fake_key, fake_containerized)  # noqa E501
         assert result == expected_command_list
 
     def test_list_key_container(self):
         fake_cluster = "fake"
         fake_containerized = "docker exec -ti ceph-mon"
+        fake_user = "fake-user"
+        fake_key = "/tmp/my-key"
         expected_command_list = [
-            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph', '--cluster',
+            ['docker', 'exec', '-ti', 'ceph-mon', 'ceph', '-n', "fake-user", '-k', "/tmp/my-key", '--cluster',  # noqa E501
                 fake_cluster, 'auth', 'ls', '-f', 'json'],
         ]
-        result = ceph_key.list_keys(fake_cluster, fake_containerized)
+        result = ceph_key.list_keys(
+            fake_cluster, fake_user, fake_key, fake_containerized)
         assert result == expected_command_list
+
+    def test_lookup_ceph_initial_entities(self):
+
+        # fake_module = MagicMock()
+        fake_ceph_dict = {"auth_dump":[{"entity":"osd.0","key":"AQAJkMhbszeBBBAA4/V1tDFXGlft1GnHJS5wWg==","caps":{"mgr":"allow profile osd","mon":"allow profile osd","osd":"allow *"}},{"entity":"osd.1","key":"AQAjkMhbshueAhAAjZec50aBgd1NObLz57SQvg==","caps":{"mgr":"allow profile osd","mon":"allow profile osd","osd":"allow *"}},{"entity":"client.admin","key":"AQDZjshbrJv6EhAAY9v6LzLYNDpPdlC3HD5KHA==","auid":0,"caps":{"mds":"allow","mgr":"allow *","mon":"allow *","osd":"allow *"}},{"entity":"client.bootstrap-mds","key":"AQDojshbc4QCHhAA1ZTrkt9dbSZRVU2GzI6U4A==","caps":{"mon":"allow profile bootstrap-mds"}},{"entity":"client.bootstrap-osd","key":"AQDjjshbYW+uGxAAyHcPCXXmVoL8VsTBI8z1Ng==","caps":{"mon":"allow profile bootstrap-osd"}},{"entity":"client.bootstrap-rbd","key":"AQDyjshb522eIhAAtAz6nUPMOdG4H9u0NgpXhA==","caps":{"mon":"allow profile bootstrap-rbd"}},{"entity":"client.bootstrap-rgw","key":"AQDtjshbDl8oIBAAq1SfSYQKDR49hJNWJVwDQw==","caps":{"mon":"allow profile bootstrap-rgw"}},{"entity":"mgr.mon0","key":"AQA0j8hbgGapORAAoDkyAvXVkM5ej4wNn4cwTQ==","caps":{"mds":"allow *","mon":"allow profile mgr","osd":"allow *"}}]}  # noqa E501
+        fake_ceph_dict_str = json.dumps(fake_ceph_dict) # convert to string
+        expected_entity_list = ['client.admin', 'client.bootstrap-mds', 'client.bootstrap-osd', 'client.bootstrap-rbd', 'client.bootstrap-rgw']  # noqa E501
+        result = ceph_key.lookup_ceph_initial_entities(fake_ceph_dict_str)
+        assert result == expected_entity_list
+
+    def test_build_key_path_admin(self):
+        fake_cluster = "fake"
+        entity = "client.admin"
+        expected_result = "/etc/ceph/fake.client.admin.keyring"
+        result = ceph_key.build_key_path(fake_cluster, entity)
+        assert result == expected_result
+
+    def test_build_key_path_bootstrap_osd(self):
+        fake_cluster = "fake"
+        entity = "bootstrap-osd"
+        expected_result = "/var/lib/ceph/bootstrap-osd/fake.keyring"
+        result = ceph_key.build_key_path(fake_cluster, entity)
+        assert result == expected_result
index a20f9a4537b445f3854d59df4840099e25df52f2..9b81aadc76c4c91642634e8b94b0842a86e017bd 100644 (file)
@@ -13,6 +13,7 @@
       - /var/lib/ceph/bootstrap-mds
       - /var/lib/ceph/bootstrap-osd
       - /var/lib/ceph/bootstrap-rbd
+      - /var/lib/ceph/bootstrap-rbd-mirror
       - /var/run/ceph
 
 - name: create ceph initial directories
index 2866ee00b76ce171ebe05e0fa87871e0d35ea064..2ddf385271926e7aa98a255f37c472cacef11d8e 100644 (file)
@@ -1,9 +1,28 @@
 ---
+- name: waiting for the monitor(s) to form the quorum...
+  command: "ceph --cluster {{ cluster }} -n mon. -k /var/lib/ceph/mon/{{ cluster }}-{{ ansible_hostname }}/keyring mon_status --format json"
+  register: ceph_health_raw
+  until: >
+    (ceph_health_raw.stdout | from_json)['state'] in ['leader', 'peon']
+  retries: "{{ handler_health_mon_check_retries }}"
+  delay: "{{ handler_health_mon_check_delay }}"
+  when:
+    - ceph_release_num[ceph_release] >= ceph_release_num.nautilus
+
+- name: fetch ceph keys in nautilus and above
+  ceph_key:
+    state: fetch_initial_keys
+    cluster: "{{ cluster }}"
+  when:
+    - ceph_release_num[ceph_release] >= ceph_release_num.nautilus
+    - cephx
+
 - name: collect admin and bootstrap keys
   command: ceph-create-keys --cluster {{ cluster }} -i {{ monitor_name }} -t 30
   changed_when: false
   check_mode: no
   when:
+    - ceph_release_num[ceph_release] < ceph_release_num.nautilus
     - cephx
 
 # NOTE (leseb): wait for mon discovery and quorum resolution
@@ -15,6 +34,7 @@
     timeout: 30
     msg: "Timed out while waiting for keyring creation. Check network settings on mon nodes."
   when:
+    - ceph_release_num[ceph_release] < ceph_release_num.nautilus
     - cephx
     - (ansible_version.major == 2 and ansible_version.minor >= 4) or
       ansible_version.major > 2
@@ -24,6 +44,7 @@
     path: /etc/ceph/{{ cluster }}.client.admin.keyring
     timeout: 30
   when:
+    - ceph_release_num[ceph_release] < ceph_release_num.nautilus
     - cephx
     - ansible_version.major == 2 and ansible_version.minor < 4