Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ceph_orch_spec: Add ceph orch apply spec feature (backport #7552) #7565

Merged
merged 1 commit into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions infrastructure-playbooks/cephadm-adopt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -931,14 +931,22 @@
- radosgw_address_block != 'subnet'

- name: Update the placement of radosgw hosts
ansible.builtin.command: >
{{ cephadm_cmd }} shell -k /etc/ceph/{{ cluster }}.client.admin.keyring --fsid {{ fsid }} --
ceph orch apply rgw {{ ansible_facts['hostname'] }}
--placement='count-per-host:{{ radosgw_num_instances }} {{ ansible_facts['nodename'] }}'
{{ rgw_subnet if rgw_subnet is defined else '' }}
--port={{ radosgw_frontend_port }}
{{ '--ssl' if radosgw_frontend_ssl_certificate else '' }}
changed_when: false
ceph_orch_apply:
fsid: "{{ fsid }}"
spec: |
service_type: rgw
service_id: {{ ansible_facts['hostname'] }}
placement:
count_per_host: {{ radosgw_num_instances }}
hosts:
- {{ ansible_facts['nodename'] }}
{{ "networks: rgw_subnet" if rgw_subnet is defined }}
spec:
rgw_frontend_port: {{ radosgw_frontend_port }}
{{ "ssl: true" if radosgw_frontend_ssl_certificate }}
extra_container_args:
- -v
- /etc/pki/ca-trust:/etc/pki/ca-trust:ro
delegate_to: "{{ groups[mon_group_name][0] }}"
environment:
CEPHADM_IMAGE: '{{ ceph_docker_registry }}/{{ ceph_docker_image }}:{{ ceph_docker_image_tag }}'
Expand Down
166 changes: 166 additions & 0 deletions library/ceph_orch_apply.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Copyright Red Hat
# SPDX-License-Identifier: Apache-2.0
# Author: Guillaume Abrioux <[email protected]>
#
# 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
from typing import List, Tuple, Dict
__metaclass__ = type

import datetime
import yaml

from ansible.module_utils.basic import AnsibleModule # type: ignore
try:
from ansible.module_utils.ca_common import exit_module, build_base_cmd_orch # type: ignore
except ImportError:
from module_utils.ca_common import exit_module, build_base_cmd_orch


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

DOCUMENTATION = '''
---
module: ceph_orch_apply
short_description: apply service spec
version_added: "2.9"
description:
- apply a service spec
options:
fsid:
description:
- the fsid of the Ceph cluster to interact with.
required: false
image:
description:
- The Ceph container image to use.
required: false
spec:
description:
- The service spec to apply
required: true
author:
- Guillaume Abrioux <[email protected]>
'''

EXAMPLES = '''
- name: apply osd spec
ceph_orch_apply:
spec: |
service_type: osd
service_id: osd
placement:
label: osds
spec:
data_devices:
all: true
'''


def parse_spec(spec: str) -> Dict:
""" parse spec string to yaml """
yaml_spec = yaml.safe_load(spec)
return yaml_spec


def retrieve_current_spec(module: AnsibleModule, expected_spec: Dict) -> Dict:
""" retrieve current config of the service """
service: str = expected_spec["service_type"]
cmd = build_base_cmd_orch(module)
cmd.extend(['ls', service, '--format=yaml'])
out = module.run_command(cmd)
if isinstance(out, str):
# if there is no existing service, cephadm returns the string 'No services reported'
return {}
else:
return yaml.safe_load(out[1])


def apply_spec(module: "AnsibleModule",
data: str) -> Tuple[int, List[str], str, str]:
cmd = build_base_cmd_orch(module)
cmd.extend(['apply', '-i', '-'])
rc, out, err = module.run_command(cmd, data=data)

if rc:
raise RuntimeError(err)

return rc, cmd, out, err


def run_module() -> None:

module_args = dict(
spec=dict(type='str', required=True),
fsid=dict(type='str', required=False),
docker=dict(type=bool,
required=False,
default=False),
image=dict(type='str', required=False)
)

module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)

startd = datetime.datetime.now()
spec = module.params.get('spec')

if module.check_mode:
exit_module(
module=module,
out='',
rc=0,
cmd=[],
err='',
startd=startd,
changed=False
)

# Idempotency check
expected = parse_spec(module.params.get('spec'))
current_spec = retrieve_current_spec(module, expected)

if not current_spec or current_spec != expected:
rc, cmd, out, err = apply_spec(module, spec)
changed = True
else:
rc = 0
cmd = []
out = ''
err = ''
changed = False

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


def main() -> None:
run_module()


if __name__ == '__main__':
main()
28 changes: 28 additions & 0 deletions module_utils/ca_common.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import os
import datetime
from typing import List
from ansible.module_utils.basic import AnsibleModule


def generate_cmd(cmd='ceph',
Expand Down Expand Up @@ -94,6 +96,32 @@ def exec_command(module, cmd, stdin=None, check_rc=False):
return rc, cmd, out, err


def build_base_cmd(module: "AnsibleModule") -> List[str]:
cmd = ['cephadm']
docker = module.params.get('docker')
image = module.params.get('image')
fsid = module.params.get('fsid')

if docker:
cmd.append('--docker')
if image:
cmd.extend(['--image', image])

cmd.append('shell')

if fsid:
cmd.extend(['--fsid', fsid])

return cmd


def build_base_cmd_orch(module: "AnsibleModule") -> List[str]:
cmd = build_base_cmd(module)
cmd.extend(['ceph', 'orch'])

return cmd


def exit_module(module, out, rc, cmd, err, startd, changed=False, diff=dict(before="", after="")): # noqa: E501
endd = datetime.datetime.now()
delta = endd - startd
Expand Down
1 change: 1 addition & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ mock
jmespath
pytest-rerunfailures
pytest-cov
setuptools
Loading