Skip to content

Commit

Permalink
library: add ceph_crush_rule module
Browse files Browse the repository at this point in the history
This adds ceph_crush_rule ansible module for replacing the command
module usage with the ceph osd crush rule commands.
This module can manage both erasure and replicated crush rules.

Signed-off-by: Dimitri Savineau <dsavinea@redhat.com>
  • Loading branch information
dsavineau authored and guits committed Dec 1, 2020
1 parent 5c4ae53 commit 2e417ab
Show file tree
Hide file tree
Showing 5 changed files with 703 additions and 8 deletions.
242 changes: 242 additions & 0 deletions library/ceph_crush_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
# 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_ceph_cmd, is_containerized, exec_command
except ImportError:
from module_utils.ca_common import exit_module, generate_ceph_cmd, is_containerized, exec_command
import datetime
import json


ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}

DOCUMENTATION = '''
---
module: ceph_crush_rule
short_description: Manage Ceph Crush Replicated/Erasure Rule
version_added: "2.8"
description:
- Manage Ceph Crush rule(s) creation, deletion and updates.
options:
name:
description:
- name of the Ceph Crush rule.
required: true
cluster:
description:
- The ceph cluster name.
required: false
default: ceph
state:
description:
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']
default: present
rule_type:
description:
- The ceph CRUSH rule type.
required: false
choices: ['replicated', 'erasure']
required: false
bucket_root:
description:
- The ceph bucket root for replicated rule.
required: false
bucket_type:
description:
- The ceph bucket type for replicated rule.
required: false
choices: ['osd', 'host', 'chassis', 'rack', 'row', 'pdu', 'pod', 'room', 'datacenter', 'zone', 'region', 'root']
device_class:
description:
- The ceph device class for replicated rule.
required: false
profile:
description:
- The ceph erasure profile for erasure rule.
required: false
author:
- Dimitri Savineau <dsavinea@redhat.com>
'''

EXAMPLES = '''
- name: create a Ceph Crush replicated rule
ceph_crush_rule:
name: foo
bucket_root: default
bucket_type: host
device_class: ssd
rule_type: replicated
- name: create a Ceph Crush erasure rule
ceph_crush_rule:
name: foo
profile: bar
rule_type: erasure
- name: get a Ceph Crush rule information
ceph_crush_rule:
name: foo
state: info
- name: delete a Ceph Crush rule
ceph_crush_rule:
name: foo
state: absent
'''

RETURN = '''# '''


def create_rule(module, container_image=None):
'''
Create a new crush replicated/erasure rule
'''

cluster = module.params.get('cluster')
name = module.params.get('name')
rule_type = module.params.get('rule_type')
bucket_root = module.params.get('bucket_root')
bucket_type = module.params.get('bucket_type')
device_class = module.params.get('device_class')
profile = module.params.get('profile')

if rule_type == 'replicated':
args = ['create-replicated', name, bucket_root, bucket_type]
if device_class:
args.append(device_class)
else:
args = ['create-erasure', name]
if profile:
args.append(profile)

cmd = generate_ceph_cmd(['osd', 'crush', 'rule'], args, cluster=cluster, container_image=container_image)

return cmd


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_ceph_cmd(['osd', 'crush', 'rule'], args, cluster=cluster, container_image=container_image)

return cmd


def remove_rule(module, container_image=None):
'''
Remove a crush rule
'''

cluster = module.params.get('cluster')
name = module.params.get('name')

args = ['rm', name]

cmd = generate_ceph_cmd(['osd', 'crush', 'rule'], args, cluster=cluster, container_image=container_image)

return cmd


def main():
module = AnsibleModule(
argument_spec=dict(
name=dict(type='str', required=True),
cluster=dict(type='str', required=False, default='ceph'),
state=dict(type='str', required=False, choices=['present', 'absent', 'info'], default='present'),
rule_type=dict(type='str', required=False, choices=['replicated', 'erasure']),
bucket_root=dict(type='str', required=False),
bucket_type=dict(type='str', required=False, choices=['osd', 'host', 'chassis', 'rack', 'row', 'pdu', 'pod',
'room', 'datacenter', 'zone', 'region', 'root']),
device_class=dict(type='str', required=False),
profile=dict(type='str', required=False)
),
supports_check_mode=True,
required_if=[
('state', 'present', ['rule_type']),
('rule_type', 'replicated', ['bucket_root', 'bucket_type']),
('rule_type', 'erasure', ['profile'])
]
)

# Gather module parameters in variables
name = module.params.get('name')
state = module.params.get('state')
rule_type = module.params.get('rule_type')

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()

if state == "present":
rc, cmd, out, err = exec_command(module, get_rule(module, container_image=container_image))
if rc != 0:
rc, cmd, out, err = exec_command(module, create_rule(module, container_image=container_image))
changed = True
else:
rule = json.loads(out)
if (rule['type'] == 1 and rule_type == 'erasure') or (rule['type'] == 3 and rule_type == 'replicated'):
module.fail_json(msg="Can not convert crush rule {} to {}".format(name, rule_type), changed=False, rc=1)

elif state == "absent":
rc, cmd, out, err = exec_command(module, get_rule(module, container_image=container_image))
if rc == 0:
rc, cmd, out, err = exec_command(module, remove_rule(module, container_image=container_image))
changed = True
else:
rc = 0
out = "Crush Rule {} doesn't exist".format(name)

elif state == "info":
rc, cmd, out, err = exec_command(module, get_rule(module, container_image=container_image))

exit_module(module=module, out=out, rc=rc, cmd=cmd, err=err, startd=startd, changed=changed)


if __name__ == '__main__':
main()
9 changes: 7 additions & 2 deletions roles/ceph-facts/tasks/get_def_crush_rule_name.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
---
- name: get current default crush rule details
command: "{{ hostvars[delegated_node | default(groups[mon_group_name][0])]['ceph_admin_command'] }} --cluster {{ cluster }} osd -f json crush rule dump"
ceph_crush_rule:
name: null
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 }}"
register: default_crush_rule_details
changed_when: false
delegate_to: "{{ delegated_node | default(groups[mon_group_name][0]) }}"
run_once: true

Expand Down
21 changes: 17 additions & 4 deletions roles/ceph-osd/tasks/crush_rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,29 @@
- osd_crush_location is defined

- name: create configured crush rules
command: "{{ hostvars[groups[mon_group_name][0]]['container_exec_cmd'] | default('') }} ceph --cluster {{ cluster }} osd crush rule {{ 'create-replicated' if item.class is defined else 'create-simple' }} {{ item.name }} {{ item.root }} {{ item.type }} {{ item.class | default('') }}"
changed_when: false
ceph_crush_rule:
name: "{{ item.name }}"
cluster: "{{ cluster }}"
rule_type: replicated
bucket_root: "{{ item.root }}"
bucket_type: "{{ item.type }}"
device_class: "{{ item.class | default(omit) }}"
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 }}"
with_items: "{{ hostvars[groups[mon_group_name][0]]['crush_rules'] | default(crush_rules) | unique }}"
delegate_to: '{{ groups[mon_group_name][0] }}'
run_once: true

- name: get id for new default crush rule
command: "{{ hostvars[groups[mon_group_name][0]]['container_exec_cmd'] | default('') }} ceph --cluster {{ cluster }} osd -f json crush rule dump {{ item.name }}"
ceph_crush_rule:
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 }}"
register: info_ceph_default_crush_rule
changed_when: false
with_items: "{{ hostvars[groups[mon_group_name][0]]['crush_rules'] | default(crush_rules) | unique }}"
delegate_to: '{{ groups[mon_group_name][0] }}'
run_once: true
Expand Down
10 changes: 8 additions & 2 deletions roles/ceph-rgw/tasks/rgw_create_pools.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@
CEPH_CONTAINER_BINARY: "{{ container_binary }}"

- name: set crush rule
command: "{{ container_exec_cmd }} ceph --connect-timeout 10 --cluster {{ cluster }} osd crush rule create-erasure {{ item.key }} {{ item.value.ec_profile }}"
ceph_crush_rule:
name: "{{ item.key }}"
cluster: "{{ cluster }}"
rule_type: erasure
profile: "{{ item.value.ec_profile }}"
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 }}"
loop: "{{ rgw_create_pools | dict2items }}"
delegate_to: "{{ groups[mon_group_name][0] }}"
changed_when: false
when:
- item.value.type is defined
- item.value.type == 'ec'
Expand Down

0 comments on commit 2e417ab

Please sign in to comment.