# SPDX-License-Identifier: MIT
---
# Some changes to corosync.conf can be applied when a cluster is running by
# instructing Corosync to reload its configuration. Other changes, however,
# require the whole cluster to be restarted. Different Corosync versions have
# different abilities as of which changes require restart and which are applied
# fine by reloading.
#
# Currently, there is no reliable way to detect if a restart is necessary or we
# would be fine with just the reload. Until then, we always restart to make
# sure all changes are applied correctly and corner cases are avoided.
#
# The plan is to implement a functionality to pcs (a CLI or library command) to
# inspect changes done to corosync.conf and decide if cluster restart is
# necessary based on Corosync version.
#
# Corosync-qdevice does not support reload, it must always be restarted to
# apply changes. If qdevice is not to be used in a cluster, we make sure it is
# stopped. The only exception to that is when qdevice is not installed at all.

- name: Get services status - detect corosync-qdevice
  service_facts:

- name: Stop cluster daemons to reload configuration
  service:
    name: "{{ item }}"
    state: stopped  # noqa no-handler
  loop:
    - pacemaker
    - corosync-qdevice
    - corosync
  when:
    - >
        __ha_cluster_distribute_corosync_conf.changed
        or __ha_cluster_distribute_corosync_authkey.changed
        or __ha_cluster_distribute_pacemaker_authkey.changed
        or (__ha_cluster_sbd_service_enable_disable.changed | d(false))
        or (__ha_cluster_distribute_sbd_config.changed | d(false))
        or (__ha_cluster_qdevice_certs_cli.changed | d(false))
        or (__ha_cluster_qdevice_certs_api.changed | d(false))
    - >
        item != 'corosync-qdevice'
        or 'corosync-qdevice.service' in ansible_facts.services


# We must always start daemons to get the cluster running on newly added nodes.

- name: Start corosync
  service:
    name: corosync
    state: started

# To prevent corner cases, always reload the config. It is sufficient to run
# the reload command on one node. Corosync then reloads config on all cluster
# nodes. If there was no change in corosync.conf, the reload is an empty
# operation.
- name: Reload corosync configuration
  command:
    cmd: corosync-cfgtool -R
  run_once: true  # noqa: run_once[task]
  when: not ansible_check_mode
  changed_when: false

- name: Start corosync-qdevice
  service:
    name: corosync-qdevice
    state: started
  when: __ha_cluster_qdevice_in_use

- name: Start pacemaker
  service:
    name: pacemaker
    state: started


- name: Wait for the cluster to fully start and form membership
  command:
    cmd: pcs cluster start --all --wait
  run_once: true  # noqa: run_once[task]
  # There is no point in waiting in check mode as there is nothing to wait for,
  # no daemons are actually starting.
  when: not ansible_check_mode
  # This is just waiting for cluster to finish starting, no change in
  # configuration / state occurs as the services were started by tasks above.
  changed_when: false


# Purge removed nodes from Pacemaker's caches

- name: List pacemaker nodes
  shell:
    cmd: >
      set -euo pipefail;
      crm_mon -X
      | xmllint --xpath '/crm_mon/nodes/node/@name' -
      | sed -E 's/\s*name="([^"]+)"\s*/\1\n/g'
    # Using crm_node like this:
    # cmd: crm_node --list | awk '{print $2}'
    # would be a preferred solution. However, crm_node doesn't show nodes
    # removed from a cluster after corosync / pacemaker restart. Therefore,
    # crm_node is no help when we want to list such nodes.
  register: __ha_cluster_list_pacemaker_nodes
  run_once: true  # noqa: run_once[task]
  # This cannot be run in the check mode as it requires cluster to be started
  # and its configuration reloaded. Check mode does not start the cluster nor
  # does it reload its configuration, it only shows the cluster would be
  # started.
  when: not ansible_check_mode
  # This only lists a part of cluster configuration.
  changed_when: false

- name: Purge removed nodes from pacemaker's cache
  command:
    cmd: pcs -- cluster node clear {{ item | quote }}
  run_once: true  # noqa: run_once[task]
  loop: "{{ __ha_cluster_list_pacemaker_nodes.stdout_lines }}"
  # This cannot be run in the check mode as it requires cluster to be started.
  # Check mode does not start the cluster, it only shows the cluster would be
  # started.
  when:
    - not ansible_check_mode
    - (item | length) > 0
    - item not in __ha_cluster_all_node_names
  changed_when:
    - not ansible_check_mode
    - (item | length) > 0
    - item not in __ha_cluster_all_node_names
