diff --git a/galaxy.yml b/galaxy.yml index 00eb8c9..a7613af 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -5,16 +5,18 @@ namespace: "ceph" name: "automation" -version: 1.0.1 +version: 1.1.0 readme: README.md authors: - - Teoman ONAY +- Teoman ONAY description: Ceph automation modules license_file: LICENSE # TO-DO: update the tags based on your content type -tags: ["linux", "tools"] -dependencies: {} +tags: [ "linux", "tools" ] +dependencies: +- "ansible.posix": ">=1.5.0" +- "community.general": "*" repository: https://github.com/ceph/ceph.automation documentation: https://docs.ceph.com/projects/ceph.automation @@ -26,9 +28,10 @@ issues: https://github.com/ceph/ceph.automation/issues # uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry', # and '.git' are always filtered. Mutually exclusive with 'manifest' build_ignore: - - .gitignore - - changelogs/.plugin-cache.yaml - - ".*" +- .gitignore +- changelogs/.plugin-cache.yaml +- ".*" + # A dict controlling use of manifest directives used in building the collection artifact. The key 'directives' is a # list of MANIFEST.in style # L(directives,https://packaging.python.org/en/latest/guides/using-manifest-in/#manifest-in-commands). The key diff --git a/playbooks/cephadm-clients.yml b/playbooks/cephadm-clients.yml new file mode 100644 index 0000000..d966b75 --- /dev/null +++ b/playbooks/cephadm-clients.yml @@ -0,0 +1,187 @@ +--- +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 +# +# Distribute keyring and conf files to a set of clients +# +# Uses ceph_defaults +# - local_client_dir: determines the dir name for the config files on the ansible host +# - ceph_defaults_ceph_client_pkgs: list of pre-req packages that must be on the client +# +# Required run-time variables +# ------------------ +# keyring : full path name of the keyring file on the admin[0] host which holds the key for the client to use +# fsid : fsid of the cluster to extract the keyring and conf from +# +# Optional run-time variables +# ------------------ +# conf : full path name of the conf file on the admin[0] host to use (undefined will trigger a minimal conf) +# ceph_defaults_client_group : ansible group name for the clients to set up +# keyring_dest : full path name of the destination where the keyring will be copied. (default: /etc/ceph/ceph.keyring) +# +# Example +# ------- +# ansible-playbook -i hosts cephadm-clients.yml -e fsid=BLA -e ceph_defaults_client_group=fs_clients -e keyring=/etc/ceph/fs.keyring +# + + +- name: Confirm local readiness + hosts: all + gather_facts: false + tasks: + - name: Confirm local readiness + run_once: true + delegate_to: localhost + block: + - name: Import_role ceph_defaults + ansible.builtin.import_role: + name: ceph_defaults + + - name: Fail if the fsid parameter is missing + ansible.builtin.fail: + msg: > + You must supply an 'fsid' parameter for the corresponding ceph cluster + when: fsid is undefined + + - name: Fail if admin group doesn't exist or is empty + ansible.builtin.fail: + msg: | + You must define a group [admin] in your inventory which provides the + keyring that you want to distribute + when: "'admin' not in groups or groups['admin'] | length < 1" + + - name: Fail if ceph_defaults_client_group is NOT in the inventory + ansible.builtin.fail: + msg: > + Variable ceph_defaults_client_group '{{ ceph_defaults_client_group }}' is not defined in the inventory + when: ceph_defaults_client_group not in groups + + - name: Fail if keyring variable is missing + ansible.builtin.fail: + msg: | + You must supply a 'keyring' variable that defines the path to the key + that you want to distribute to your client machines + when: keyring is not defined + + +- name: Confirm admin host is ready + hosts: admin[0] + become: true + gather_facts: false + tasks: + - name: Check fsid is present on {{ inventory_hostname }} + ansible.builtin.stat: + path: /var/lib/ceph/{{ fsid }} + register: fsid_stat + + - name: Fail if fsid is not present + ansible.builtin.fail: + msg: > + The given fsid ({{ fsid }}), is not present in /var/lib/ceph on {{ inventory_hostname }} + when: + - not fsid_stat.stat.exists | bool + - not fsid_stat.stat.isdir | bool + + - name: Check keyring status on {{ inventory_hostname }} + ansible.builtin.stat: + path: "{{ keyring }}" + register: keyring_stat + + - name: Fail if keyring not found on {{ inventory_hostname }} + ansible.builtin.fail: + msg: > + The keyring path provided '{{ keyring }}' can not be found on {{ inventory_hostname }} + when: not keyring_stat.stat.exists | bool + + - name: Check conf is OK to use + ansible.builtin.stat: + path: "{{ conf }}" + register: conf_stat + when: conf is defined + + - name: Fail if conf supplied is not on {{ inventory_hostname }} + ansible.builtin.fail: + msg: | + The conf file '{{ conf }}' can not be found on {{ inventory_hostname }} + when: + - conf is defined + - not conf_stat.stat.exists | bool + - not conf_stat.stat.isreg | bool + + +- name: Assemble client payload + hosts: admin[0] + become: true + gather_facts: false + tasks: + - name: Import_role ceph_defaults + ansible.builtin.import_role: + name: ceph_defaults + + - name: Slurp the keyring + ansible.builtin.slurp: + src: "{{ keyring }}" + register: client_keyring + no_log: true + + - name: Slurp the conf if it's supplied + ansible.builtin.slurp: + src: "{{ conf }}" + register: ceph_config + when: + - conf is defined + - conf | length > 0 + + - name: Create minimal conf as a default + ansible.builtin.command: cephadm shell -- ceph config generate-minimal-conf + register: minimal_ceph_config + when: conf is undefined + + +- name: Distribute client configuration + hosts: "{{ ceph_defaults_client_group }}" + become: true + gather_facts: true + tasks: + - name: Import_role ceph_defaults + ansible.builtin.import_role: + name: ceph_defaults + + - name: Install ceph-common on rhel + ansible.builtin.command: dnf install --allowerasing --assumeyes ceph-common + changed_when: false + register: result + until: result is succeeded + when: ansible_facts['os_family'] == 'RedHat' + + - name: Install ceph client prerequisites if needed + ansible.builtin.package: + name: "{{ ceph_defaults_ceph_client_pkgs }}" + state: present + register: result + until: result is succeeded + + - name: Copy configuration and keyring files to the clients + ansible.builtin.copy: + content: "{{ item.content }}" + dest: "{{ item.dest }}" + owner: ceph + group: ceph + mode: '0600' + backup: true + loop: + - { content: "{{ hostvars[groups['admin'][0]]\ + ['client_keyring']['content'] | b64decode }}", + dest: "{{ keyring_dest | default('/etc/ceph/ceph.keyring') }}", + copy_file: True } + - { content: "{{ hostvars[groups['admin'][0]]\ + ['minimal_ceph_config']['stdout'] | default('') }}{{ '\n' }}", + dest: '/etc/ceph/ceph.conf', + copy_file: "{{ conf is undefined }}" } + - { content: "{{ hostvars[groups['admin'][0]]\ + ['ceph_config']['content'] | default('') | b64decode }}", + dest: '/etc/ceph/ceph.conf', + copy_file: "{{ hostvars[groups['admin'][0]]\ + ['ceph_config']['skipped'] is undefined }}" } + when: item.copy_file | bool + no_log: true diff --git a/playbooks/cephadm-distribute-ssh-key.yml b/playbooks/cephadm-distribute-ssh-key.yml new file mode 100644 index 0000000..cde2b0d --- /dev/null +++ b/playbooks/cephadm-distribute-ssh-key.yml @@ -0,0 +1,73 @@ +--- +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 +# Author: Guillaume Abrioux +# +# This playbook copies an SSH public key to a specified user on remote hosts. +# +# Required run-time variables +# ------------------ +# admin_node : The name of a node with enough privileges to call `cephadm get-pub-key` command. +# (usually the bootstrap node). +# +# Optional run-time variables +# ------------------ +# fsid : The fsid of the Ceph cluster. +# cephadm_ssh_user : ssh username on remote hosts. +# cephadm_pubkey_path : Full path name of the ssh public key file *on the ansible controller host*. +# If not passed, the playbook will assume it has to get the key from `cephadm get-pub-key` command. +# +# Example +# ------- +# ansible-playbook -i hosts cephadm-distribute-ssh-key.yml -e cephadm_ssh_user=foo -e cephadm_pubkey_path=/home/cephadm/ceph.key -e admin_node=ceph-node0 +# +# ansible-playbook -i hosts cephadm-distribute-ssh-key.yml -e cephadm_ssh_user=foo -e admin_node=ceph-node0 + +- hosts: all + become: true + gather_facts: false + tasks: + - name: Fail if admin_node is not defined + ansible.builtin.fail: + msg: "You must set the variable admin_node" + run_once: true + delegate_to: localhost + when: admin_node is undefined + + - name: Get ssh public key from a file on the Ansible controller host + when: cephadm_pubkey_path is defined + block: + - name: Get details about {{ cephadm_pubkey_path }} + ansible.builtin.stat: + path: "{{ cephadm_pubkey_path }}" + register: cephadm_pubkey_path_stat + delegate_to: localhost + run_once: true + + - name: Fail if {{ cephadm_pubkey_path }} doesn't exist + ansible.builtin.fail: + msg: "{{ cephadm_pubkey_path }} doesn't exist or is invalid." + run_once: true + delegate_to: localhost + when: + - not cephadm_pubkey_path_stat.stat.exists | bool + or not cephadm_pubkey_path_stat.stat.isfile | bool + + - name: Get the cephadm ssh pub key + ansible.builtin.command: "cephadm shell {{ '--fsid ' + fsid if fsid is defined else '' }} ceph cephadm get-pub-key" + changed_when: false + run_once: true + register: cephadm_get_pub_key + delegate_to: "{{ admin_node }}" + when: cephadm_pubkey_path is undefined + + - name: Allow ssh public key for {{ cephadm_ssh_user | default('root') }} account + ansible.posix.authorized_key: + user: "{{ cephadm_ssh_user | default('root') }}" + key: "{{ lookup('file', cephadm_pubkey_path) if cephadm_pubkey_path is defined else cephadm_get_pub_key.stdout }}" + + - name: Set cephadm ssh user to {{ cephadm_ssh_user }} + ansible.builtin.command: "cephadm shell {{ '--fsid ' + fsid if fsid is defined else '' }} ceph cephadm set-user {{ cephadm_ssh_user | default('root') }}" + changed_when: false + run_once: true + delegate_to: "{{ admin_node }}" \ No newline at end of file diff --git a/playbooks/cephadm-preflight.yml b/playbooks/cephadm-preflight.yml new file mode 100644 index 0000000..3cf7799 --- /dev/null +++ b/playbooks/cephadm-preflight.yml @@ -0,0 +1,312 @@ +--- +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 +# +# This playbook configures the Ceph repository. +# It also installs some prerequisites (podman, lvm2, chronyd, cephadm, ...) +# +# Usage: +# +# ansible-playbook -i cephadm-preflight.yml +# +# You can limit the execution to a set of hosts by using `--limit` option: +# +# ansible-playbook -i cephadm-preflight.yml --limit +# +# You can override variables using `--extra-vars` parameter: +# +# ansible-playbook -i cephadm-preflight.yml --extra-vars "ceph_default_ceph_origin=rhcs" +# + +- name: Variables validations + ansible.builtin.import_playbook: validate/preflight.yml + +- hosts: all + become: true + gather_facts: true + vars: + repos_to_disable: "{{ range(5, ceph_defaults_ceph_rhcs_version) | map('string') | map('regex_replace', '^', 'rhceph_') | map('regex_replace', '$', '-tools-for-rhel-' + ansible_facts['distribution_major_version'] + '-' + ansible_facts['architecture'] + '-rpms') | list }}" + packages_to_uninstall: + - ceph-mds + - ceph-mgr + - ceph-mon + - ceph-osd + - ceph-radosgw + - rbd-mirror + tasks: + - name: Import_role ceph_defaults + ansible.builtin.import_role: + name: ceph_defaults + + - name: Redhat family of OS related tasks + when: ansible_facts['os_family'] == 'RedHat' + block: + - name: Rhcs related tasks + when: ceph_default_ceph_origin == 'rhcs' + block: + - name: Enable red hat ceph storage tools repository + community.general.rhsm_repository: + name: "rhceph-{{ ceph_defaults_ceph_rhcs_version }}-tools-for-rhel-{{ ansible_facts['distribution_major_version'] }}-{{ ansible_facts['architecture'] }}-rpms" + + - name: disable older rhceph repositories if any on RHEL{{ansible_facts['distribution_major_version']}} + when: ansible_facts['distribution_major_version'] <= '8' + rhsm_repository: + name: "{{ repos_to_disable }}" + state: absent + + - name: Enable ceph package repositories + when: ceph_defaults_ceph_origin in ['community', 'ibm'] + block: + - name: Set_fact _ceph_repo + ansible.builtin.set_fact: + _ceph_repo: + name: ceph_stable + description: "{{ 'Ceph Stable repo' if ceph_defaults_ceph_origin == 'community' else 'IBM Ceph repo' }}" + rpm_key: "{{ ceph_defaults_ceph_stable_key if ceph_defaults_ceph_origin == 'community' else ceph_defaults_ceph_ibm_key }}" + baseurl: "{{ ceph_defaults_ceph_community_repo_baseurll if ceph_defaults_ceph_origin == 'community' else ceph_defaults_ceph_ibm_repo_baseurl }}" + paths: "{{ [ 'noarch', '$basearch' ] if ceph_defaults_ceph_origin == 'community' else [ '$basearch' ] }}" + + - name: Configure ceph repository key + ansible.builtin.rpm_key: + key: "{{ _ceph_repo.rpm_key }}" + state: present + register: result + until: result is succeeded + + - name: Configure ceph stable repository + ansible.builtin.yum_repository: + name: "ceph_stable_{{ item }}" + description: "{{ _ceph_repo.description }} - {{ item }}" + gpgcheck: true + state: present + gpgkey: "{{ _ceph_repo.rpm_key }}" + baseurl: "{{ _ceph_repo.baseurl }}/{{ item }}" + file: "ceph_stable_{{ item }}" + priority: '2' + register: result + until: result is succeeded + loop: "{{ _ceph_repo.paths }}" + + - name: Enable repo from shaman - dev + when: ceph_defaults_ceph_origin == 'shaman' + block: + - name: Fetch ceph development repository + ansible.builtin.uri: + url: + "https://shaman.ceph.com/api/repos/ceph/\ + {{ ceph_defaults_ceph_dev_branch }}/\ + {{ ceph_defaults_ceph_dev_sha1 }}/\ + centos/{{ ansible_facts['distribution_major_version'] }}/\ + repo?arch={{ ansible_facts['architecture'] }}" + return_content: true + register: ceph_dev_yum_repo + + - name: Configure ceph development repository + ansible.builtin.copy: + content: "{{ ceph_dev_yum_repo.content }}" + dest: /etc/yum.repos.d/ceph-dev.repo + owner: root + group: root + mode: '0644' + backup: true + + - name: Remove ceph_stable repositories + ansible.builtin.yum_repository: + name: '{{ item }}' + file: ceph_stable + state: absent + with_items: + - ceph_stable + - ceph_stable_noarch + + - name: Enable custom repo + when: ceph_defaults_ceph_origin == 'custom' + block: + - name: Set_fact ceph_custom_repositories + ansible.builtin.set_fact: + ceph_custom_repositories: + - name: Ceph_custom + description: Ceph custom repo + gpgcheck: "{{ 'yes' if custom_repo_gpgkey is defined else 'no' }}" + state: "{{ custom_repo_state | default('present') }}" + gpgkey: "{{ custom_repo_gpgkey | default(omit) }}" + baseurl: "{{ custom_repo_url }}" + enabled: "{{ custom_repo_enabled | default(1) }}" + file: ceph_custom + priority: '2' + when: ceph_custom_repositories is undefined + + - name: Setup custom repositories + ansible.builtin.yum_repository: + name: "{{ item.name }}" + description: "{{ item.description }}" + state: "{{ item.state | default(omit) }}" + gpgcheck: "{{ item.gpgcheck | default(omit) }}" + gpgkey: "{{ item.gpgkey | default(omit) }}" + baseurl: "{{ item.baseurl }}" + file: "{{ item.ceph_custom | default(omit) }}" + priority: "{{ item.priority | default(omit) }}" + enabled: "{{ item.enabled | default(omit) }}" + register: result + until: result is succeeded + loop: "{{ ceph_custom_repositories }}" + + - name: Install epel-release + when: ansible_facts['distribution'] != 'RedHat' + block: + - name: Enable required CentOS repository for epel + ansible.builtin.command: dnf config-manager --set-enabled "{{ 'powertools' if ansible_facts['distribution_major_version'] == '8' else 'crb' }}" + changed_when: false + + - name: install epel package + package: + name: epel-release + state: present + register: result + until: result is succeeded + + - name: Remove remaining local services ceph packages + ansible.builtin.dnf: + name: "{{ packages_to_uninstall }}" + state: absent + autoremove: false + + - name: Install ceph-common on rhel + ansible.builtin.package: + name: ceph-common + state: "{{ (ceph_defaults_upgrade_ceph_packages | bool) | ternary('latest', 'present') }}" + register: result + until: result is succeeded + + - name: Install prerequisites packages on servers + ansible.builtin.package: + name: "{{ ceph_defaults_ceph_pkgs + infra_pkgs }}" + state: "{{ (ceph_defaults_upgrade_ceph_packages | bool) | ternary('latest', 'present') }}" + register: result + until: result is succeeded + when: group_names != [ceph_defaults_client_group] + + - name: Install prerequisites packages on clients + ansible.builtin.package: + name: "{{ ceph_defaults_ceph_client_pkgs }}" + state: "{{ (ceph_defaults_upgrade_ceph_packages | bool) | ternary('latest', 'present') }}" + register: result + until: result is succeeded + when: ceph_defaults_client_group in group_names + + + - name: Ensure chronyd is running + ansible.builtin.service: + name: chronyd + state: started + enabled: true + + - name: Ubuntu related tasks + when: ansible_facts['distribution'] == 'Ubuntu' + block: + - name: Enable repo from download.ceph.com + block: + - name: Prevent ceph certificate error + ansible.builtin.apt: + name: ca-certificates + state: latest + update_cache: true + register: result + until: result is succeeded + + - name: Configure ceph community repository stable key + ansible.builtin.apt_key: + url: "{{ ceph_defaults_ceph_stable_key }}" + state: present + + - name: Configure Ceph community repository + when: ceph_defaults_ceph_origin == 'community' + ansible.builtin.apt_repository: + repo: "deb https://download.ceph.com/debian-{{ ceph_defaults_ceph_release }}/ {{ ansible_facts['distribution_release'] }} main" + state: present + filename: ceph + update_cache: false + + - name: Configure Ceph testing repository + when: ceph_defaults_ceph_origin == 'testing' + ansible.builtin.apt_repository: + repo: "deb https://download.ceph.com/debian-testing/ {{ ansible_facts['distribution_release'] }} main" + state: present + filename: ceph + update_cache: false + + - name: Configure Ceph custom repositories + when: ceph_defaults_ceph_origin == 'custom' + ansible.builtin.apt_repository: + repo: "deb {{ item.baseurl }}/ {{ ansible_facts['distribution_release'] }} {{ item.components }}" + state: "{{ item.state | default(omit) }}" + filename: ceph_custom + update_cache: false + loop: "{{ ceph_custom_repositories }}" + + - name: Install prerequisites packages + ansible.builtin.apt: + name: "{{ ['python3','chrony'] + ceph_defaults_ceph_pkgs }}" + state: "{{ (ceph_defaults_upgrade_ceph_packages | bool) | ternary('latest', 'present') }}" + update_cache: true + register: result + until: result is succeeded + + - name: Ensure chronyd is running + ansible.builtin.service: + name: chronyd + state: started + enabled: true + + - name: Install container engine + block: + - name: Install podman + when: ansible_facts['distribution_version'] is version('20.10', '>=') + ansible.builtin.apt: + name: podman + state: present + update_cache: true + register: result + until: result is succeeded + + - name: Install docker + when: ansible_facts['distribution_version'] is version('20.10', '<') + block: + - name: Uninstall old version packages + ansible.builtin.apt: + name: "{{ item }}" + state: absent + loop: + - docker + - docker-engine + - docker.io + - containerd + - runc + + - name: Configure docker repository key + ansible.builtin.apt_key: + url: "https://download.docker.com/linux/ubuntu/gpg" + state: present + + - name: Setup docker repository + ansible.builtin.apt_repository: + repo: "deb https://download.docker.com/linux/ubuntu {{ ansible_facts['distribution_release'] }} stable" + state: present + filename: docker + update_cache: false + + - name: Install docker + ansible.builtin.apt: + name: "{{ item }}" + state: present + update_cache: true + register: result + until: result is succeeded + loop: + - docker-ce + - docker-ce-cli + - containerd.io + +- name: Set insecure container registry in /etc/containers/registries.conf + ansible.builtin.import_playbook: cephadm-set-container-insecure-registries.yml + when: set_insecure_registries | default(false) | bool diff --git a/playbooks/cephadm-purge-cluster.yml b/playbooks/cephadm-purge-cluster.yml new file mode 100644 index 0000000..d8bab1c --- /dev/null +++ b/playbooks/cephadm-purge-cluster.yml @@ -0,0 +1,168 @@ +--- +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 +# +# This playbook purges a Ceph cluster managed with cephadm +# +# You must define a group [admin] in your inventory with a node where +# the admin keyring is present at /etc/ceph/ceph.client.admin.keyring +# +# Usage: +# +# ansible-playbook -i cephadm-purge-cluster.yml -e fsid= -e infra_pkgs_purge= + + +- name: Check local prerequisites are in place + hosts: all + gather_facts: false + become: true + any_errors_fatal: true + tasks: + - name: Check local prerequisites are in place + run_once: true + delegate_to: localhost + block: + - name: Fail if fsid was not provided + ansible.builtin.fail: + msg: | + You must provide the cluster fsid to be purged. + e.g. ansible-playbook -i cephadm-purge-cluster.yml -e fsid= + when: fsid is undefined + + - name: Fail if admin group doesn't exist or is empty + ansible.builtin.fail: + msg: | + You must define a group [admin] in your inventory and add a node where + admin keyring is present at /etc/ceph/ceph.client.admin.keyring + when: "'admin' not in groups or groups['admin'] | length < 1" + +- name: Check keyring is present on the admin host + hosts: admin[0] + gather_facts: false + any_errors_fatal: true + tasks: + - name: Check /etc/ceph/ceph.client.admin.keyring + ansible.builtin.stat: + path: /etc/ceph/ceph.client.admin.keyring + register: admin_keyring_stat + + - name: Fail if /etc/ceph/ceph.client.admin.keyring is not present + ansible.builtin.fail: + msg: > + You must have /etc/ceph/ceph.client.admin.keyring present on {{ inventory_hostname }} + when: not admin_keyring_stat.stat.exists | bool + + +- name: Check cluster hosts have cephadm and the required fsid {{ fsid }} + hosts: all,!{{ ceph_defaults_client_group }} + gather_facts: false + become: true + any_errors_fatal: true + tasks: + - name: Import_role ceph_defaults + ansible.builtin.import_role: + name: ceph_defaults + + - name: Check cephadm binary is available + ansible.builtin.command: which cephadm + register: cephadm_exists + changed_when: false + failed_when: false + + - name: Fail if cephadm is not available + ansible.builtin.fail: + msg: | + The cephadm binary is missing on {{ inventory_hostname }}. To purge the cluster you must have cephadm installed + on ALL ceph hosts. Install manually or use the preflight playbook. + when: + - cephadm_exists.rc + + - name: Check fsid directory given is valid across the cluster + ansible.builtin.stat: + path: /var/lib/ceph/{{ fsid }} + register: fsid_exists + + - name: Fail if the fsid directory is missing + ansible.builtin.fail: + msg: | + The fsid directory '/var/lib/ceph/{{ fsid }}' can not be found on {{ inventory_hostname }} + Is the fsid correct? + when: + - not fsid_exists.stat.exists | bool + + +- name: Confirm whether user really wants to purge the cluster + hosts: all + gather_facts: false + become: false + + vars_prompt: + - name: Ireallymeanit + prompt: | + + Are you sure you want to purge the cluster with fsid={{ fsid }} ? + default: 'no' + private: false + + tasks: + - name: Exit playbook, if user did not mean to purge cluster + run_once: true + delegate_to: localhost + ansible.builtin.fail: + msg: | + Exiting cephadm-purge-cluster playbook, cluster was NOT purged. + To purge the cluster, either say 'yes' at the prompt or use `-e ireallymeanit=yes` + on the command line when invoking the playbook + when: ireallymeanit != 'yes' + + +- name: Disable cephadm operations + hosts: "admin[0]" + become: true + gather_facts: false + tasks: + - name: Disable cephadm + ansible.builtin.command: "cephadm shell --fsid {{ fsid }} -- ceph mgr module disable cephadm" + changed_when: false + + +- name: Purge ceph daemons from all hosts in the cluster + hosts: all,!{{ ceph_defaults_client_group }} + become: true + gather_facts: false + any_errors_fatal: true + tasks: + - name: Import_role ceph_defaults + ansible.builtin.import_role: + name: ceph_defaults + + - name: Purge ceph daemons + ansible.builtin.command: "cephadm rm-cluster --force --zap-osds --fsid {{ fsid }}" + changed_when: false + + +- name: Remove ceph packages + hosts: all + become: true + gather_facts: false + any_errors_fatal: true + tasks: + - name: Import_role ceph_defaults + ansible.builtin.import_role: + name: ceph_defaults + + - name: Remove ceph packages on ceph nodes + ansible.builtin.package: + name: "{{ ceph_defaults_ceph_pkgs | union(infra_pkgs | intersect(infra_pkgs_purge | default([]))) }}" + state: absent + register: result + until: result is succeeded + when: group_names != [ceph_defaults_client_group] + + - name: Remove ceph packages on client nodes + ansible.builtin.package: + name: ceph-common + state: absent + register: result + until: result is succeeded + when: group_names == [ceph_defaults_client_group] diff --git a/playbooks/cephadm-set-container-insecure-registries.yml b/playbooks/cephadm-set-container-insecure-registries.yml new file mode 100644 index 0000000..2623b72 --- /dev/null +++ b/playbooks/cephadm-set-container-insecure-registries.yml @@ -0,0 +1,37 @@ +--- +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 +# Author: Guillaume Abrioux +# +# Usage: +# +# ansible-playbook -i cephadm-set-container-insecure-registries.yml -e insecure_registry= +# +# eg: +# +# ansible-playbook -i hosts cephadm-set-container-insecure-registries.yml -e insecure_registry=localhost:5000 + +- name: Variables validations + ansible.builtin.import_playbook: validate/insecure-registries.yml + +- name: set insecure registry + hosts: all + become: true + gather_facts: false + tasks: + - name: Fail if insecure_registry is undefined + ansible.builtin.fail: + msg: "'insecure_registry' is undefined" + when: insecure_registry is undefined + + - name: Add registry as insecure registry in registries.conf + ansible.builtin.blockinfile: + path: "{{ registries_conf_path | default('/etc/containers/registries.conf') }}" + state: present + marker: "# {mark} cephadm-ansible managed : {{ insecure_registry }}" + create: true + mode: '0644' + block: | + [[registry]] + location = '{{ insecure_registry }}' + insecure = true diff --git a/playbooks/rocksdb-resharding.yml b/playbooks/rocksdb-resharding.yml new file mode 100644 index 0000000..2468884 --- /dev/null +++ b/playbooks/rocksdb-resharding.yml @@ -0,0 +1,123 @@ +--- +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 +# Author: Guillaume Abrioux +# +# This playbook reshards the rocksDB database for a given OSD +# +# Usage: +# +# ansible-playbook -i rocksdb-resharding.yml -e osd_id=0 -e admin_node=ceph-mon0 +# +# Required run-time variables +# ------------------ +# osd_id : the id of the OSD where you want to reshard its corresponding rocksdb database. +# admin_node : the name of a node with enough privileges to stop/start +# daemons via `cephadm shell ceph orch daemon` command. +# (usually the bootstrap node). +# +# Optional run-time variables +# ------------------ +# fsid : the fsid of the cluster. +# rocksdb_sharding_parameters : the rocksdb sharding parameter to set. Default is 'm(3) p(3,0-12) O(3,0-13) L P'. +# docker : bool to be set in order to use docker engine instead. Default is False. + +- name: Rocksdb-resharding + hosts: all + become: true + gather_facts: false + tasks: + - name: Check prerequisites + run_once: true + delegate_to: localhost + block: + - name: Fail if osd_id is not defined + ansible.builtin.fail: + msg: "you must provide 'osd_id' variable" + when: osd_id is undefined + + - name: Fail if admin_node is not defined + ansible.builtin.fail: + msg: "you must pass 'admin_node' variable" + when: admin_node is not defined + + - name: Fail if osd_id isn't an id + ansible.builtin.fail: + msg: "osd_id must be an id" + when: not osd_id is regex('^\d+$') + + - name: Set_fact cephadm_cmd + ansible.builtin.set_fact: + cephadm_cmd: "cephadm {{ '--docker' if docker | default(False) | bool else '' }} shell ceph" + + + - name: Test connectivity to admin node + ansible.builtin.ping: + delegate_to: "{{ admin_node }}" + run_once: true + + - name: Get details about the osd daemon + delegate_to: "{{ admin_node }}" + block: + - name: Get cluster fsid + ansible.builtin.command: "{{ cephadm_cmd }} fsid" + register: fsid + changed_when: false + when: fsid is not defined + + - name: Set_fact fsid + ansible.builtin.set_fact: + fsid: "{{ fsid.stdout }}" + when: fsid.stdout is defined + + - name: Get container image currently used by osd container + ansible.builtin.command: "{{ cephadm_cmd }} orch ps --daemon_type osd --daemon_id {{ osd_id }} --format json" + changed_when: false + register: ceph_orch_ps + retries: 120 + delay: 1 + until: (ceph_orch_ps.stdout | from_json)[0]['status_desc'] == 'running' + + - name: Set_fact container_image, container_host + ansible.builtin.set_fact: + container_image: "{{ (ceph_orch_ps.stdout | from_json)[0]['container_image_name'] }}" + container_host: "{{ (ceph_orch_ps.stdout | from_json)[0]['hostname'] }}" + + - name: Stop the osd + ceph.automation.ceph_orch_daemon: + fsid: "{{ fsid }}" + state: stopped + daemon_id: "{{ osd_id }}" + daemon_type: osd + + - name: Set_fact ceph_cmd + ansible.builtin.set_fact: + ceph_bluestore_tool_cmd: "{{ container_binary | default('podman') }} run --rm --privileged --entrypoint=ceph-bluestore-tool -v /var/run/ceph/{{ fsid }}:/var/run/ceph:z -v /var/log/ceph/{{ fsid }}:/var/log/ceph:z -v /var/lib/ceph/{{ fsid }}/crash:/var/lib/ceph/crash:z -v /var/lib/ceph/{{ fsid }}/osd.{{ osd_id }}:/var/lib/ceph/osd/ceph-{{ osd_id }}:z -v /var/lib/ceph/{{ fsid }}/osd.{{ osd_id }}/config:/etc/ceph/ceph.conf:z -v /dev:/dev -v /run/udev:/run/udev -v /sys:/sys -v /var/lib/ceph/{{ fsid }}/selinux:/sys/fs/selinux:ro -v /run/lvm:/run/lvm -v /run/lock/lvm:/run/lock/lvm {{ container_image }} --path /var/lib/ceph/osd/ceph-{{ osd_id }}" + + - name: Resharding operations + delegate_to: "{{ container_host }}" + run_once: true + block: + - name: Check fs consistency with fsck before resharding + ansible.builtin.command: "{{ ceph_bluestore_tool_cmd }} fsck" + changed_when: false + + - name: Show current sharding + ansible.builtin.command: "{{ ceph_bluestore_tool_cmd }} show-sharding" + changed_when: false + + - name: Reshard + ansible.builtin.command: "{{ ceph_bluestore_tool_cmd }} --sharding=\"{{ rocksdb_sharding_parameters | default('m(3) p(3,0-12) O(3,0-13) L P') }}\" reshard" + changed_when: false + + - name: Check fs consistency with fsck after resharding + ansible.builtin.command: "{{ ceph_bluestore_tool_cmd }} fsck" + changed_when: false + + - name: Restart the osd + ceph.automation.ceph_orch_daemon: + fsid: "{{ fsid }}" + state: started + daemon_id: "{{ osd_id }}" + daemon_type: osd + delegate_to: "{{ admin_node }}" diff --git a/playbooks/validate/insecure-registries.yml b/playbooks/validate/insecure-registries.yml new file mode 100644 index 0000000..cc6c4d2 --- /dev/null +++ b/playbooks/validate/insecure-registries.yml @@ -0,0 +1,18 @@ +--- +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 +# Author: Guillaume Abrioux + +- name: Insecure_registries + hosts: all + become: false + gather_facts: false + tasks: + - name: Fail if insecure_registry is undefined + run_once: true + delegate_to: localhost + ansible.builtin.fail: + msg: "'insecure_registry' is undefined, it must be set when 'set_insecure_registries' is 'true'." + when: + - set_insecure_registries | default(false) | bool + - insecure_registry is undefined diff --git a/playbooks/validate/preflight.yml b/playbooks/validate/preflight.yml new file mode 100644 index 0000000..e9d3078 --- /dev/null +++ b/playbooks/validate/preflight.yml @@ -0,0 +1,37 @@ +--- +# Copyright Red Hat +# SPDX-License-Identifier: Apache-2.0 +# Author: Guillaume Abrioux + +- ansible.builtin.import_playbook: insecure-registries.yml + when: set_insecure_registries | default(false) | bool + +- name: Preflight + hosts: all + become: false + gather_facts: false + tasks: + - name: Preflight + run_once: true + delegate_to: localhost + block: + - name: Import_role ceph_defaults + ansible.builtin.import_role: + name: ceph_defaults + + - name: Fail when ceph_defaults_ceph_origin is custom with no repository defined + ansible.builtin.fail: + msg: "You must define 'ceph_custom_repositories' or 'custom_repo_url' when ceph_defaults_ceph_origin is 'custom'" + when: + - ceph_defaults_ceph_origin == 'custom' + - (custom_repo_url is undefined or custom_repo_url == '') + - ceph_custom_repositories is undefined + + - name: Fail if baseurl is not defined for ceph_custom_repositories + ansible.builtin.fail: + msg: "One repository is missing a required parameter: name, description, baseurl." + loop: "{{ ceph_custom_repositories }}" + when: + - ceph_defaults_ceph_origin == 'custom' + - ceph_custom_repositories is defined + - (item.baseurl is undefined or item.name is undefined or item.description is undefined) diff --git a/roles/ceph_defaults/README.md b/roles/ceph_defaults/README.md new file mode 100644 index 0000000..885e494 --- /dev/null +++ b/roles/ceph_defaults/README.md @@ -0,0 +1,3 @@ +# Ansible role: ceph_defaults + +Documentation is available at http://docs.ceph.com/cephadm-ansible/. diff --git a/roles/ceph_defaults/defaults/main.yml b/roles/ceph_defaults/defaults/main.yml new file mode 100644 index 0000000..a2eb53b --- /dev/null +++ b/roles/ceph_defaults/defaults/main.yml @@ -0,0 +1,25 @@ +--- +ceph_defaults_ceph_origin: community +ceph_defaults_ceph_dev_branch: main +ceph_defaults_ceph_dev_sha1: latest +ceph_defaults_ceph_rhcs_version: 8 +ceph_defaults_ceph_ibm_version: 8 +ceph_defaults_ceph_mirror: https://download.ceph.com +ceph_defaults_ceph_stable_key: https://download.ceph.com/keys/release.asc +ceph_defaults_ceph_community_repo_baseurll: "{{ ceph_defaults_ceph_mirror }}/rpm-{{ ceph_defaults_ceph_release }}/el{{ ansible_facts['distribution_major_version'] }}/" +ceph_defaults_ceph_ibm_repo_baseurl: "https://public.dhe.ibm.com/ibmdl/export/pub/storage/ceph/{{ ceph_defaults_ceph_ibm_version }}/rhel{{ ansible_facts['distribution_major_version'] }}/" +ceph_defaults_ceph_ibm_key: https://public.dhe.ibm.com/ibmdl/export/pub/storage/ceph/RPM-GPG-KEY-IBM-CEPH +ceph_defaults_ceph_release: squid +ceph_defaults_upgrade_ceph_packages: false +ceph_defaults_ceph_pkgs: + - cephadm + - ceph-common +ceph_defaults_ceph_client_pkgs: + - chrony + - ceph-common +ceph_defaults_infra_pkgs: + - chrony + - podman + - lvm2 + - sos +ceph_defaults_client_group: clients diff --git a/roles/ceph_defaults/meta/main.yml b/roles/ceph_defaults/meta/main.yml new file mode 100644 index 0000000..4210556 --- /dev/null +++ b/roles/ceph_defaults/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + company: Red Hat + author: Guillaume Abrioux + description: Handles ceph-ansible default vars for all roles + license: Apache + min_ansible_version: 2.15 + platforms: + - name: Ubuntu + versions: + - bionic + - name: EL + versions: + - 9 + galaxy_tags: + - system +dependencies: []