From 7d80fc1f30db36acdd22f5a6406a0f5df7b0004c Mon Sep 17 00:00:00 2001 From: Michal Arbet Date: Fri, 3 Jan 2025 12:25:07 +0100 Subject: [PATCH] Add support for key in CephX keyring definition The cephadm_key Ansible module now supports defining key for CephX keyrings, enabling more flexible key management and integration within Ceph clusters. This feature is particularly useful with Kolla-Ansible, as it allows clear definition of keys with specific capabilities and key values. Keys can now be stored securely in Git configurations or Vault, simplifying management across both projects. Resolves stackhpc#165 --- plugins/module_utils/cephadm_common.py | 33 ++++++++----- plugins/modules/cephadm_key.py | 67 ++++++++++++++++++++++---- roles/keys/tasks/main.yml | 3 +- 3 files changed, 82 insertions(+), 21 deletions(-) diff --git a/plugins/module_utils/cephadm_common.py b/plugins/module_utils/cephadm_common.py index 3d1f42a..d6cc159 100644 --- a/plugins/module_utils/cephadm_common.py +++ b/plugins/module_utils/cephadm_common.py @@ -20,20 +20,31 @@ import datetime -def generate_ceph_cmd(sub_cmd, args): +def generate_ceph_cmd(sub_cmd, args, key_entry=None): ''' Generate 'ceph' command line to execute ''' - - cmd = [ - 'cephadm', - '--timeout', - '60', - 'shell', - '--', - 'ceph', - ] - cmd.extend(sub_cmd + args) + cmd = [] + + if key_entry: + cmd = [ + 'cephadm', + 'shell', + '--', + 'bash', + '-c', + f'echo -e "{key_entry}" | ceph {" ".join(sub_cmd)} {" ".join(args)}' + ] + else: + cmd = [ + 'cephadm', + '--timeout', + '60', + 'shell', + '--', + 'ceph', + ] + cmd.extend(sub_cmd + args) return cmd diff --git a/plugins/modules/cephadm_key.py b/plugins/modules/cephadm_key.py index 0819276..afbd21b 100644 --- a/plugins/modules/cephadm_key.py +++ b/plugins/modules/cephadm_key.py @@ -57,6 +57,12 @@ default: {} required: false type: dict + key: + description: + - Secret value of the key. If specified, this key will be + used explicitly instead of being generated. + required: false + type: str output_format: description: - The key output format when retrieving the information of an @@ -150,6 +156,25 @@ def create_key(name, caps): # noqa: E501 return cmd +def create_key_by_import(name, caps, key): + ''' + Create a CephX key by import + ''' + cmd = [] + + caps_cli = [] + for k, v in caps.items(): + caps_cli.append(f'caps {k} = "{v}"') + + key_entry = f"[{name}]\n\tkey = {key}\n\t" + "\n\t".join(caps_cli) + + sub_cmd = ['auth', 'import'] + args = ['-i', '-'] + cmd.append(generate_ceph_cmd(sub_cmd=sub_cmd, args=args, key_entry=key_entry)) + + return cmd + + def update_key(name, caps): ''' Update the caps of a CephX key @@ -168,6 +193,15 @@ def update_key(name, caps): return cmd +def update_key_by_import(name, caps, key=None): + ''' + Update a CephX key by re-importing it + ''' + cmd = create_key_by_import(name, caps, key) + + return cmd + + def delete_key(name): ''' Delete a CephX key @@ -264,6 +298,7 @@ def run_module(): state=dict(type='str', required=False, default='present', choices=['present', 'absent', # noqa: E501 'list', 'info']), # noqa: E501 caps=dict(type='dict', required=False, default={}), + key=dict(type='str', required=False, default=None), output_format=dict(type='str', required=False, default='json', choices=['json', 'plain', 'xml', 'yaml']) # noqa: E501 ) @@ -276,6 +311,7 @@ def run_module(): state = module.params['state'] name = module.params.get('name') caps = module.params.get('caps') + key = module.params.get('key') output_format = module.params.get('output_format') changed = False @@ -318,20 +354,33 @@ def run_module(): result["rc"] = 0 module.exit_json(**result) else: - rc, cmd, out, err = exec_commands(module, update_key(name, caps)) # noqa: E501 + if key and key != _key: + rc, cmd, out, err = exec_commands( + module, update_key_by_import(name, caps, key)) # noqa: E501 + else: + rc, cmd, out, err = exec_commands( + module, update_key(name, caps)) # noqa: E501 if rc != 0: - result["msg"] = "Couldn't update caps for {0}".format(name) + result["stdout"] = "Couldn't update {0}".format(name) result["stderr"] = err - module.fail_json(**result) + module.exit_json(**result) changed = True else: - rc, cmd, out, err = exec_commands(module, create_key(name, caps)) # noqa: E501 - if rc != 0: - result["msg"] = "Couldn't create {0}".format(name) - result["stderr"] = err - module.fail_json(**result) - changed = True + if key: + rc, cmd, out, err = exec_commands(module, create_key_by_import(name, caps, key)) + if rc != 0: + result["stdout"] = "Couldn't import {0}".format(name) + result["stderr"] = err + module.exit_json(**result) + changed = True + else: + rc, cmd, out, err = exec_commands(module, create_key(name, caps)) # noqa: E501 + if rc != 0: + result["stdout"] = "Couldn't create {0}".format(name) + result["stderr"] = err + module.exit_json(**result) + changed = True elif state == "absent": rc, cmd, out, err = exec_commands( diff --git a/roles/keys/tasks/main.yml b/roles/keys/tasks/main.yml index c2b6614..50ec4c1 100644 --- a/roles/keys/tasks/main.yml +++ b/roles/keys/tasks/main.yml @@ -4,7 +4,8 @@ name: "{{ item.name }}" state: "{{ item.state | default(omit) }}" caps: "{{ item.caps }}" - secret: "{{ item.key | default(omit) }}" + key: "{{ item.key | default(omit) }}" with_items: "{{ cephadm_keys }}" delegate_to: "{{ groups['mons'][0] }}" run_once: true + no_log: "{{ item.key is defined }}"