223 lines
7.3 KiB
YAML
223 lines
7.3 KiB
YAML
---
|
||
# tasks/main.yml
|
||
- name: Apply new Patroni configuration
|
||
ansible.builtin.uri:
|
||
url: "http://{{ patroni_host }}:{{ patroni_api_port }}/config"
|
||
method: PATCH
|
||
body: "{{ new_config | to_json }}"
|
||
body_format: json
|
||
status_code: 200
|
||
headers:
|
||
Content-Type: "application/json"
|
||
register: apply_result
|
||
changed_when: apply_result.status == 200
|
||
notify: "config applied"
|
||
|
||
- name: Wait for config propagation # noqa: no-handler
|
||
ansible.builtin.wait_for:
|
||
timeout: 30
|
||
delay: 5
|
||
when: apply_result is changed
|
||
|
||
- name: Check for pending restarts # noqa: no-handler
|
||
when: apply_result is changed
|
||
run_once: true
|
||
block:
|
||
- name: Get cluster status with retry
|
||
ansible.builtin.uri:
|
||
url: "http://{{ patroni_host }}:{{ patroni_api_port }}/cluster"
|
||
method: GET
|
||
return_content: yes
|
||
status_code: 200
|
||
register: cluster_status
|
||
until: cluster_status.json is defined
|
||
retries: 3
|
||
delay: 2
|
||
delegate_to: localhost
|
||
run_once: true
|
||
|
||
- name: Check restart flags
|
||
ansible.builtin.set_fact:
|
||
needs_restart: >-
|
||
{{
|
||
(cluster_status.json.members |
|
||
map(attribute='pending_restart', default=false) |
|
||
select('equalto', true) | list | length > 0) or
|
||
(cluster_status.json.members |
|
||
map(attribute='tags.pending_restart', default=false) |
|
||
select('equalto', true) | list | length > 0)
|
||
}}
|
||
node_names: "{{ cluster_status.json.members | map(attribute='name') | list }}"
|
||
node_info: >-
|
||
{% set info = {} %}
|
||
{% for member in cluster_status.json.members %}
|
||
{% set _ = info.update({member.name: {'role': member.role}}) %}
|
||
{% endfor %}
|
||
{{ info }}
|
||
run_once: true
|
||
rescue:
|
||
- name: Set no restart needed
|
||
ansible.builtin.set_fact:
|
||
needs_restart: false
|
||
run_once: true
|
||
|
||
|
||
- name: Display restart warning
|
||
ansible.builtin.debug:
|
||
msg: |
|
||
{% if needs_restart %}
|
||
{% if autorestart %}
|
||
================================================
|
||
ПРЕДУПРЕЖДЕНИЕ: АВТОМАТИЧЕСКИЙ ПЕРЕЗАПУСК КЛАСТЕРА
|
||
================================================
|
||
Следующие ноды будут перезапущены:
|
||
{% for node in node_names %}
|
||
- {{ node }} ({{ node_info[node].role | default('UNKNOWN') }})
|
||
{% endfor %}
|
||
|
||
Для отмены нажмите Ctrl+C в течение 10 секунд
|
||
{% else %}
|
||
============================================
|
||
ВНИМАНИЕ: НЕОБХОДИМ РУЧНОЙ ПЕРЕЗАПУСК КЛАСТЕРА
|
||
============================================
|
||
Выполните на одной из нод:
|
||
|
||
patronictl restart -c /etc/patrony.yml {{ node_names | join(' ') }}
|
||
|
||
Ноды для перезапуска:
|
||
{% for node in node_names %}
|
||
- {{ node }} ({{ node_info[node].role | default('UNKNOWN') }})
|
||
{% endfor %}
|
||
{% endif %}
|
||
{% else %}
|
||
================================
|
||
ПЕРЕЗАГРУЗКА НЕ ТРЕБУЕТСЯ
|
||
================================
|
||
{% endif %}
|
||
delegate_to: localhost
|
||
run_once: true
|
||
when:
|
||
- needs_restart is defined
|
||
- node_names is defined
|
||
- node_info is defined
|
||
|
||
- name: Confirm automatic restart
|
||
ansible.builtin.pause:
|
||
prompt: "Подтвердите автоматический перезапуск кластера (Enter - продолжить, Ctrl+C - отмена)"
|
||
seconds: 10
|
||
when:
|
||
- needs_restart | default(false)
|
||
- autorestart | default(false)
|
||
delegate_to: localhost
|
||
run_once: true
|
||
|
||
- name: Execute cluster restart
|
||
when:
|
||
- needs_restart | default(false)
|
||
- autorestart | bool
|
||
- cluster_status is defined
|
||
- cluster_status.json is defined
|
||
- cluster_status.json.members is defined
|
||
run_once: true
|
||
block:
|
||
- name: Find nodes needing restart
|
||
ansible.builtin.set_fact:
|
||
nodes_to_restart: >-
|
||
{%
|
||
set nodes = []
|
||
%}{%
|
||
for member in cluster_status.json.members
|
||
%}{%
|
||
if member.pending_restart is defined and member.pending_restart or
|
||
member.tags.pending_restart is defined and member.tags.pending_restart
|
||
%}{%
|
||
set _ = nodes.append(member)
|
||
%}{%
|
||
endif
|
||
%}{%
|
||
endfor
|
||
%}{{
|
||
nodes
|
||
}}
|
||
|
||
- name: Restart nodes via API
|
||
ansible.builtin.uri:
|
||
url: "http://{{ item.host }}:{{ patroni_api_port }}/restart"
|
||
method: POST
|
||
body_format: json
|
||
body:
|
||
restart_pending: true
|
||
timeout: 60
|
||
status_code: [200, 503]
|
||
loop: "{{ nodes_to_restart | default([]) }}"
|
||
loop_control:
|
||
label: "{{ item.name }}"
|
||
register: restart_results
|
||
ignore_errors: yes
|
||
changed_when: >
|
||
restart_results.status == 200 or
|
||
restart_results.status == 503
|
||
|
||
- name: Wait for cluster stabilization
|
||
block:
|
||
- name: Check cluster status until stable
|
||
ansible.builtin.uri:
|
||
url: "http://{{ patroni_host }}:{{ patroni_api_port }}/cluster"
|
||
method: GET
|
||
return_content: yes
|
||
status_code: 200
|
||
register: cluster_health
|
||
until: >
|
||
cluster_health.json.members |
|
||
selectattr('state', 'match', '^(running|streaming)$') |
|
||
list | length == cluster_health.json.members | length
|
||
retries: 12
|
||
delay: 10
|
||
delegate_to: localhost
|
||
run_once: true
|
||
|
||
- name: Show restart results
|
||
ansible.builtin.debug:
|
||
msg: |
|
||
========================
|
||
РЕЗУЛЬТАТЫ ПЕРЕЗАГРУЗКИ
|
||
========================
|
||
Нода: {{ item.item.name }}
|
||
Роль: {{ item.item.role }}
|
||
Статус: {% if item.status == 200 %}Успешно перезапущена{% elif item.status == 503 %}Перезапуск в процессе{% else %}Ошибка (код {{ item.status }}){% endif %}
|
||
Время выполнения: {{ item.elapsed }} сек
|
||
{% if item.item.pending_restart_reason is defined %}
|
||
Причина перезагрузки:
|
||
{% for param, values in item.item.pending_restart_reason.items() %}
|
||
- {{ param }}: было {{ values.old_value }}, стало {{ values.new_value }}
|
||
{% endfor %}
|
||
{% endif %}
|
||
------------------------
|
||
loop: "{{ restart_results.results | default([]) }}"
|
||
loop_control:
|
||
label: ""
|
||
run_once: true
|
||
|
||
- name: Archive old configurations # noqa: no-handler
|
||
when: apply_result is changed
|
||
run_once: true
|
||
block:
|
||
- name: Find old config files
|
||
ansible.builtin.find:
|
||
path: "{{ config_dir }}"
|
||
pattern: "*-config.yaml"
|
||
age: "10s"
|
||
register: old_configs
|
||
delegate_to: localhost
|
||
connection: local
|
||
|
||
- name: Remove excess configs
|
||
ansible.builtin.file:
|
||
path: "{{ item.path }}"
|
||
state: absent
|
||
loop: "{{ (old_configs.files | sort(attribute='mtime'))[:-10] }}"
|
||
when: old_configs.matched > 10
|
||
delegate_to: localhost
|
||
connection: local
|
||
notify: "Log cleanup"
|