with_items: "{{ keys_to_delete }}"
- name: Info ceph key(s)
- ceph_key:
+ ceph_key_info:
name: "{{ item }}"
state: info
cluster: "{{ cluster }}"
with_items: "{{ keys_to_info }}"
- name: List ceph key(s)
- ceph_key:
+ ceph_key_info:
state: list
cluster: "{{ cluster }}"
containerized: "{{ container_exec_cmd | default(False) }}"
inventory_hostname in groups.get(rbdmirror_group_name, [])
- name: Get the client.admin keyring
- ceph_key:
+ ceph_key_info:
name: client.admin
cluster: "{{ cluster }}"
output_format: plain
If 'present' is used, the module creates a rule if it doesn't
exist or update it if it already exists.
If 'absent' is used, the module will simply delete the rule.
- If 'info' is used, the module will return all details about the
- existing rule (json formatted).
required: false
- choices: ['present', 'absent', 'info']
+ choices: ['present', 'absent']
default: present
rule_type:
description:
argument_spec=dict(
name=dict(type='str', required=False),
cluster=dict(type='str', required=False, default='ceph'),
- state=dict(type='str', required=False, choices=['present', 'absent', 'info'], default='present'), # noqa: E501
+ state=dict(type='str', required=False, choices=['present', 'absent'], default='present'), # noqa: E501
rule_type=dict(type='str', required=False, choices=['replicated', 'erasure']), # noqa: E501
bucket_root=dict(type='str', required=False),
bucket_type=dict(type='str', required=False, choices=['osd', 'host', 'chassis', 'rack', 'row', 'pdu', 'pod', # noqa: E501
--- /dev/null
+# Copyright 2020, Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+from ansible.module_utils.basic import AnsibleModule
+try:
+ from ansible.module_utils.ca_common import exit_module, \
+ generate_cmd, \
+ is_containerized, \
+ exec_command
+except ImportError:
+ from module_utils.ca_common import exit_module, \
+ generate_cmd, \
+ is_containerized, \
+ exec_command
+import datetime
+
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+DOCUMENTATION = '''
+---
+module: ceph_crush_rule_info
+short_description: Lists Ceph Crush Replicated/Erasure Rules
+version_added: "2.8"
+description:
+ - Retrieces Ceph Crush rule(s).
+options:
+ name:
+ description:
+ - name of the Ceph Crush rule. If state is 'info' - empty string
+ can be provided as a value to get all crush rules
+ required: true
+ cluster:
+ description:
+ - The ceph cluster name.
+ required: false
+ default: ceph
+author:
+ - Teoman ONAY <@asm0deuz>
+'''
+
+EXAMPLES = '''
+- name: get a Ceph Crush rule information
+ ceph_crush_rule_info:
+ name: foo
+'''
+
+RETURN = '''# '''
+
+
+def get_rule(module, container_image=None):
+ '''
+ Get existing crush rule
+ '''
+
+ cluster = module.params.get('cluster')
+ name = module.params.get('name')
+
+ args = ['dump', name, '--format=json']
+
+ cmd = generate_cmd(sub_cmd=['osd', 'crush', 'rule'],
+ args=args,
+ cluster=cluster,
+ container_image=container_image)
+
+ return cmd
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ name=dict(type='str', required=False),
+ cluster=dict(type='str', required=False, default='ceph'),
+ ),
+ supports_check_mode=True,
+ )
+
+ if module.check_mode:
+ module.exit_json(
+ changed=False,
+ stdout='',
+ stderr='',
+ rc=0,
+ start='',
+ end='',
+ delta='',
+ )
+
+ startd = datetime.datetime.now()
+ changed = False
+
+ # will return either the image name or None
+ container_image = is_containerized()
+
+ rc, cmd, out, err = exec_command(module, get_rule(module, container_image=container_image)) # noqa: E501
+
+ exit_module(module=module, out=out, rc=rc, cmd=cmd, err=err, startd=startd, changed=changed) # noqa: E501
+
+
+if __name__ == '__main__':
+ main()
from ansible.module_utils.basic import AnsibleModule
try:
from ansible.module_utils.ca_common import generate_cmd, \
- is_containerized, \
- container_exec, \
- fatal
+ is_containerized, \
+ container_exec, \
+ fatal
except ImportError:
from module_utils.ca_common import generate_cmd, \
- is_containerized, \
- container_exec, \
- fatal
+ is_containerized, \
+ container_exec, \
+ fatal
import datetime
import json
import os
If 'absent' is used, the module will simply delete the keyring.
If 'list' is used, the module will list all the keys and will
return a json output.
- If 'info' is used, the module will return in a json format the
- description of a given keyring.
If 'generate_secret' is used, the module will simply output a cephx keyring.
required: false
- choices: ['present', 'update', 'absent', 'list', 'info', 'fetch_initial_keys', 'generate_secret']
+ choices: ['present', 'update', 'absent', 'fetch_initial_keys', 'generate_secret']
default: present
caps:
description:
name: "my_key"
state: absent
-- name: info cephx key
- ceph_key:
- name: "my_key""
- state: info
-
-- name: info cephx admin key (plain)
- ceph_key:
- name: client.admin
- output_format: plain
- state: info
- register: client_admin_key
-
-- name: list cephx keys
- ceph_key:
- state: list
-
- name: fetch cephx keys
ceph_key:
state: fetch_initial_keys
cluster=dict(type='str', required=False, default='ceph'),
name=dict(type='str', required=False),
state=dict(type='str', required=False, default='present', choices=['present', 'update', 'absent', # noqa: E501
- 'list', 'info', 'fetch_initial_keys', 'generate_secret']), # noqa: E501
+ 'fetch_initial_keys', 'generate_secret']), # noqa: E501
caps=dict(type='dict', required=False, default=None),
secret=dict(type='str', required=False, default=None, no_log=True),
import_key=dict(type='bool', required=False, default=True),
output_format = module.params.get('output_format')
# Can't use required_if with 'name' for some reason...
- if state in ['present', 'absent', 'update', 'info'] and not name:
+ if state in ['present', 'absent', 'update'] and not name:
fatal(f'"state" is "{state}" but "name" is not defined.', module)
changed = False
else:
rc = 0
- elif state == "info":
- rc, cmd, out, err = exec_commands(
- module, info_key(cluster, name, user, user_key_path, output_format, container_image)) # noqa: E501
-
- elif state == "list":
- rc, cmd, out, err = exec_commands(
- module, list_keys(cluster, user, user_key_path, container_image))
-
elif state == "fetch_initial_keys":
hostname = socket.gethostname().split('.', 1)[0]
user = "mon."
--- /dev/null
+# Copyright 2018, Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+from ansible.module_utils.basic import AnsibleModule
+try:
+ from ansible.module_utils.ca_common import generate_cmd, \
+ is_containerized, \
+ fatal
+except ImportError:
+ from module_utils.ca_common import generate_cmd, \
+ is_containerized, \
+ fatal
+import datetime
+import os
+
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+DOCUMENTATION = '''
+---
+module: ceph_key_info
+
+author: Teoman ONAY <@asM0deuz>
+
+short_description: Manage Cephx key(s)
+
+version_added: "2.6"
+
+description:
+ - Manage CephX creation, deletion and updates.
+ It can also list and get information about keyring(s).
+options:
+ cluster:
+ description:
+ - The ceph cluster name.
+ required: false
+ default: ceph
+ name:
+ description:
+ - name of the CephX key
+ required: true
+ user:
+ description:
+ - entity used to perform operation.
+ It corresponds to the -n option (--name)
+ required: false
+ user_key:
+ description:
+ - the path to the keyring corresponding to the
+ user being used.
+ It corresponds to the -k option (--keyring)
+ state:
+ description:
+ If 'list' is used, the module will list all the keys and will
+ return a json output.
+ If 'info' is used, the module will return in a json format the
+ description of a given keyring.
+ required: false
+ choices: ['list', 'info']
+ default: list
+ output_format:
+ description:
+ - The key output format when retrieving the information of an
+ entity.
+ required: false
+ default: json
+'''
+
+EXAMPLES = '''
+
+- name: info cephx key
+ ceph_key:
+ name: "my_key""
+ state: info
+
+- name: info cephx admin key (plain)
+ ceph_key:
+ name: client.admin
+ output_format: plain
+ state: info
+ register: client_admin_key
+
+- name: list cephx keys
+ ceph_key:
+ state: list
+'''
+
+RETURN = '''# '''
+
+
+CEPH_INITIAL_KEYS = ['client.admin', 'client.bootstrap-mds', 'client.bootstrap-mgr', # noqa: E501
+ 'client.bootstrap-osd', 'client.bootstrap-rbd', 'client.bootstrap-rbd-mirror', 'client.bootstrap-rgw'] # noqa: E501
+
+
+def info_key(cluster, name, user, user_key, output_format, container_image=None): # noqa: E501
+ '''
+ Get information about a CephX key
+ '''
+
+ cmd_list = []
+
+ args = [
+ 'get',
+ name,
+ '-f',
+ output_format,
+ ]
+
+ cmd_list.append(generate_cmd(sub_cmd=['auth'],
+ args=args,
+ cluster=cluster,
+ user=user,
+ user_key=user_key,
+ container_image=container_image))
+
+ return cmd_list
+
+
+def list_keys(cluster, user, user_key, container_image=None):
+ '''
+ List all CephX keys
+ '''
+
+ cmd_list = []
+
+ args = [
+ 'ls',
+ '-f',
+ 'json',
+ ]
+
+ cmd_list.append(generate_cmd(sub_cmd=['auth'],
+ args=args,
+ cluster=cluster,
+ user=user,
+ user_key=user_key,
+ container_image=container_image))
+
+ return cmd_list
+
+
+def exec_commands(module, cmd_list):
+ '''
+ Execute command(s)
+ '''
+
+ for cmd in cmd_list:
+ rc, out, err = module.run_command(cmd)
+ if rc != 0:
+ return rc, cmd, out, err
+
+ return rc, cmd, out, err
+
+
+def run_module():
+ module_args = dict(
+ cluster=dict(type='str', required=False, default='ceph'),
+ name=dict(type='str', required=False),
+ state=dict(type='str', required=False, default='info', choices=['list', 'info']), # noqa: E501
+ user=dict(type='str', required=False, default='client.admin'),
+ user_key=dict(type='str', required=False, default=None),
+ output_format=dict(type='str', required=False, default='json', choices=['json', 'plain', 'xml', 'yaml']) # noqa: E501
+ )
+
+ module = AnsibleModule(
+ argument_spec=module_args,
+ supports_check_mode=True,
+ add_file_common_args=True,
+ )
+
+ # Gather module parameters in variables
+ state = module.params['state']
+ name = module.params.get('name')
+ cluster = module.params.get('cluster')
+ user = module.params.get('user')
+ user_key = module.params.get('user_key')
+ output_format = module.params.get('output_format')
+
+ # Can't use required_if with 'name' for some reason...
+ if state in ['info'] and not name:
+ fatal(f'"state" is "{state}" but "name" is not defined.', module)
+
+ changed = False
+ cmd = ''
+ rc = 0
+ out = ''
+ err = ''
+
+ result = dict(
+ changed=changed,
+ stdout='',
+ stderr='',
+ rc=0,
+ start='',
+ end='',
+ delta='',
+ )
+
+ if module.check_mode:
+ module.exit_json(**result)
+
+ startd = datetime.datetime.now()
+
+ # will return either the image name or None
+ container_image = is_containerized()
+
+ if not user_key:
+ user_key_filename = '{}.{}.keyring'.format(cluster, user)
+ user_key_dir = '/etc/ceph'
+ user_key_path = os.path.join(user_key_dir, user_key_filename)
+ else:
+ user_key_path = user_key
+
+ if state == "info":
+ rc, cmd, out, err = exec_commands(
+ module, info_key(cluster, name, user, user_key_path, output_format, container_image)) # noqa: E501
+
+ elif state == "list":
+ rc, cmd, out, err = exec_commands(
+ module, list_keys(cluster, user, user_key_path, container_image))
+
+ endd = datetime.datetime.now()
+ delta = endd - startd
+
+ result = dict(
+ cmd=cmd,
+ start=str(startd),
+ end=str(endd),
+ delta=str(delta),
+ rc=rc,
+ stdout=out.rstrip("\r\n"),
+ stderr=err.rstrip("\r\n"),
+ changed=changed,
+ )
+
+ if rc != 0:
+ module.fail_json(msg='non-zero return code', **result)
+
+ module.exit_json(**result)
+
+
+def main():
+ run_module()
+
+
+if __name__ == '__main__':
+ main()
- copy_admin_key | bool
block:
- name: Get keys from monitors
- ceph_key:
+ ceph_key_info:
name: client.admin
cluster: "{{ cluster }}"
output_format: plain
no_log: "{{ no_log_on_ceph_key_tasks }}"
- name: Get keys from monitors
- ceph_key:
+ ceph_key_info:
name: client.crash
cluster: "{{ cluster }}"
output_format: plain
no_log: "{{ no_log_on_ceph_key_tasks }}"
- name: Get keys from monitors
- ceph_key:
+ ceph_key_info:
name: client.ceph-exporter
cluster: "{{ cluster }}"
output_format: plain
---
- name: Get current default crush rule details
- ceph_crush_rule:
+ ceph_crush_rule_info:
cluster: "{{ cluster }}"
- state: info
environment:
CEPH_CONTAINER_IMAGE: "{{ ceph_docker_registry + '/' + ceph_docker_image + ':' + ceph_docker_image_tag if containerized_deployment | bool else None }}"
CEPH_CONTAINER_BINARY: "{{ container_binary }}"
- /var/lib/ceph/mds/{{ cluster }}-{{ ansible_facts['hostname'] }}
- name: Get keys from monitors
- ceph_key:
+ ceph_key_info:
name: "{{ item.name }}"
cluster: "{{ cluster }}"
output_format: plain
- { 'name': "mgr.{{ ansible_facts['hostname'] }}", 'path': "/var/lib/ceph/mgr/{{ cluster }}-{{ ansible_facts['hostname'] }}/keyring", 'copy_key': true }
- name: Get keys from monitors
- ceph_key:
+ ceph_key_info:
name: "{{ item.name }}"
cluster: "{{ cluster }}"
output_format: plain
when: cephx | bool
block:
- name: Check if monitor initial keyring already exists
- ceph_key:
+ ceph_key_info:
name: mon.
cluster: "{{ cluster }}"
user: mon.
- item.create | default(False) | bool
- name: Get keys from monitors
- ceph_key:
+ ceph_key_info:
name: "{{ item.name }}"
cluster: "{{ cluster }}"
output_format: plain
- groups.get(mon_group_name, []) | length > 0
block:
- name: Get keys from monitors
- ceph_key:
+ ceph_key_info:
name: "{{ item.name }}"
cluster: "{{ cluster }}"
output_format: plain
- /var/lib/ceph/osd/
- name: Get keys from monitors
- ceph_key:
+ ceph_key_info:
name: "{{ item.name }}"
cluster: "{{ cluster }}"
output_format: plain
run_once: true
- name: Get id for new default crush rule
- ceph_crush_rule:
+ ceph_crush_rule_info:
name: "{{ item.name }}"
cluster: "{{ cluster }}"
- state: info
environment:
CEPH_CONTAINER_IMAGE: "{{ ceph_docker_registry + '/' + ceph_docker_image + ':' + ceph_docker_image_tag if containerized_deployment | bool else None }}"
CEPH_CONTAINER_BINARY: "{{ container_binary }}"
- cephx | bool
block:
- name: Get client.bootstrap-rbd-mirror from ceph monitor
- ceph_key:
+ ceph_key_info:
name: client.bootstrap-rbd-mirror
cluster: "{{ cluster }}"
output_format: plain
secret: "{{ ceph_rbd_mirror_local_user_secret | default('') }}" }
- name: Get client.rbd-mirror keyring from ceph monitor
- ceph_key:
+ ceph_key_info:
name: "client.rbd-mirror.{{ ansible_facts['hostname'] }}"
cluster: "{{ cluster }}"
output_format: plain
---
- name: Get keys from monitors
- ceph_key:
+ ceph_key_info:
name: "{{ item.name }}"
cluster: "{{ cluster }}"
output_format: plain
when: cephx | bool
- name: Get keys from monitors
- ceph_key:
+ ceph_key_info:
name: "client.rgw.{{ rgw_zone }}.{{ ansible_facts['hostname'] }}.{{ item.instance_name }}"
cluster: "{{ cluster }}"
output_format: plain
import pytest
import ca_test_common
import ceph_crush_rule
+import ceph_crush_rule_info
fake_cluster = 'ceph'
fake_container_binary = 'podman'
def test_get_non_existing_rule(self, m_run_command, m_exit_json):
ca_test_common.set_module_args({
'name': fake_name,
- 'state': 'info'
})
m_exit_json.side_effect = ca_test_common.exit_json
rc = 2
m_run_command.return_value = rc, stdout, stderr
with pytest.raises(ca_test_common.AnsibleExitJson) as result:
- ceph_crush_rule.main()
+ ceph_crush_rule_info.main()
result = result.value.args[0]
assert not result['changed']
def test_get_existing_rule(self, m_run_command, m_exit_json):
ca_test_common.set_module_args({
'name': fake_name,
- 'state': 'info'
})
m_exit_json.side_effect = ca_test_common.exit_json
rc = 0
m_run_command.return_value = rc, stdout, stderr
with pytest.raises(ca_test_common.AnsibleExitJson) as result:
- ceph_crush_rule.main()
+ ceph_crush_rule_info.main()
result = result.value.args[0]
assert not result['changed']
def test_get_all_rules(self, m_run_command, m_exit_json):
ca_test_common.set_module_args({
'name': str(),
- 'state': 'info'
})
m_exit_json.side_effect = ca_test_common.exit_json
rc = 0
m_run_command.return_value = rc, stdout, stderr
with pytest.raises(ca_test_common.AnsibleExitJson) as result:
- ceph_crush_rule.main()
+ ceph_crush_rule_info.main()
result = result.value.args[0]
assert not result['changed']
def test_with_container(self, m_run_command, m_exit_json):
ca_test_common.set_module_args({
'name': fake_name,
- 'state': 'info'
})
m_exit_json.side_effect = ca_test_common.exit_json
rc = 0
m_run_command.return_value = rc, stdout, stderr
with pytest.raises(ca_test_common.AnsibleExitJson) as result:
- ceph_crush_rule.main()
+ ceph_crush_rule_info.main()
result = result.value.args[0]
assert not result['changed']
import pytest
import ca_test_common
import ceph_key
+import ceph_key_info
@mock.patch.dict(os.environ, {'CEPH_CONTAINER_BINARY': 'docker'})
assert result == expected_result
@mock.patch('ansible.module_utils.basic.AnsibleModule.exit_json')
- @mock.patch('ceph_key.exec_commands')
+ @mock.patch('ceph_key_info.exec_commands')
@pytest.mark.parametrize('output_format', ['json', 'plain', 'xml', 'yaml'])
def test_state_info(self, m_exec_commands, m_exit_json, output_format):
ca_test_common.set_module_args({"state": "info",
'exported keyring for client.admin')
with pytest.raises(ca_test_common.AnsibleExitJson) as result:
- ceph_key.run_module()
+ ceph_key_info.run_module()
result = result.value.args[0]
assert not result['changed']
m_fail_json.side_effect = ca_test_common.fail_json
with pytest.raises(ca_test_common.AnsibleFailJson) as result:
- ceph_key.run_module()
+ ceph_key_info.run_module()
result = result.value.args[0]
assert result['msg'] == 'value of output_format must be one of: json, plain, xml, yaml, got: {}'.format(invalid_format)