Refactoring + final touches
This commit is contained in:
@@ -8,16 +8,44 @@
|
||||
vars:
|
||||
# Dynamically detect if we are in Single-Node or Cluster mode
|
||||
is_single_node: "{{ groups['proxmox'] | length == 1 }}"
|
||||
|
||||
# Existing clustering variables
|
||||
# standalone_mode can be set manually in group_vars to bypass cluster/linstor tasks
|
||||
# even with multiple nodes in inventory (e.g. independent nodes for the same customer)
|
||||
_skip_cluster: "{{ (is_single_node | bool) or (standalone_mode | default(false) | bool) }}"
|
||||
|
||||
# Corosync clustering IPs
|
||||
corosync_ip: "{{ priv_ip | default(ansible_host) }}"
|
||||
master_corosync_ip: "{{ hostvars[groups['proxmox'][0]]['priv_ip'] | default(hostvars[groups['proxmox'][0]]['ansible_host']) }}"
|
||||
is_master: "{{ inventory_hostname == groups['proxmox'][0] }}"
|
||||
|
||||
tasks:
|
||||
# ==========================================
|
||||
# PHASE 0: P2P PRIVATE NETWORK
|
||||
# ==========================================
|
||||
- name: Configure P2P Private Network Bridge
|
||||
ansible.builtin.include_role:
|
||||
name: p2p_network
|
||||
when:
|
||||
- not _skip_cluster | bool
|
||||
- priv_nic_1 is defined
|
||||
- priv_nic_2 is defined
|
||||
- priv_ip is defined
|
||||
|
||||
# ==========================================
|
||||
# PHASE 1: REPOSITORIES & LICENSING (PVE 9 / Trixie)
|
||||
# ==========================================
|
||||
- name: Disable IPv6 system-wide
|
||||
ansible.posix.sysctl:
|
||||
name: "{{ item }}"
|
||||
value: "1"
|
||||
state: present
|
||||
sysctl_file: /etc/sysctl.d/99-disable-ipv6.conf
|
||||
reload: true
|
||||
loop:
|
||||
- net.ipv6.conf.all.disable_ipv6
|
||||
- net.ipv6.conf.default.disable_ipv6
|
||||
- net.ipv6.conf.lo.disable_ipv6
|
||||
when: disable_ipv6 | default(false) | bool
|
||||
|
||||
- name: Apply Proxmox Enterprise Key
|
||||
ansible.builtin.command: pvesubscription set -k {{ pve_enterprise_key }}
|
||||
when: pve_enterprise_key | default('') | length > 0
|
||||
@@ -53,27 +81,36 @@
|
||||
signed_by: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
state: "{{ 'absent' if pve_enterprise_key | default('') | length > 0 else 'present' }}"
|
||||
|
||||
- name: Remove Ceph Enterprise Repository (Unused in Linstor)
|
||||
- name: Remove Ceph Enterprise Repository (Unused in Linstor setups)
|
||||
ansible.builtin.file:
|
||||
path: /etc/apt/sources.list.d/ceph.sources
|
||||
state: absent
|
||||
when: pve_enterprise_key | default('') | length == 0
|
||||
|
||||
- name: Add Linstor Repositories (Skipped in Standalone Node)
|
||||
- name: Add Linstor Repositories
|
||||
ansible.builtin.include_role:
|
||||
name: linstor_repo
|
||||
when:
|
||||
when:
|
||||
- deploy_linstor | bool
|
||||
- not is_standalone | bool
|
||||
- not _skip_cluster | bool
|
||||
|
||||
# ==========================================
|
||||
# PHASE 2: UPDATES & NAG REMOVAL
|
||||
# ==========================================
|
||||
|
||||
# NOTE: async/poll used here to prevent SSH timeout during long dist-upgrade.
|
||||
# The task is submitted asynchronously (up to 1h) and polled every 30s.
|
||||
- name: Update APT cache and run dist-upgrade
|
||||
ansible.builtin.apt:
|
||||
update_cache: yes
|
||||
update_cache: true
|
||||
upgrade: dist
|
||||
autoremove: yes
|
||||
autoremove: true
|
||||
lock_timeout: 300 # Wait up to 5 min for dpkg lock from other apt processes
|
||||
dpkg_options: 'force-confnew,force-confdef' # Non-interactive config file handling
|
||||
environment:
|
||||
DEBIAN_FRONTEND: noninteractive # Prevent debconf from blocking on prompts
|
||||
async: 3600 # Allow up to 1 hour for the full upgrade
|
||||
poll: 30 # Check progress every 30 seconds
|
||||
|
||||
- name: Remove Proxmox Subscription Nag (JS Patch)
|
||||
ansible.builtin.replace:
|
||||
@@ -82,12 +119,12 @@
|
||||
replace: "void({ //\\1"
|
||||
notify: Restart pveproxy
|
||||
|
||||
- name: Install Linstor Packages (Skipped in Single Node)
|
||||
- name: Install Linstor Packages
|
||||
ansible.builtin.include_role:
|
||||
name: linstor_install
|
||||
when:
|
||||
when:
|
||||
- deploy_linstor | bool
|
||||
- not is_single_node | bool
|
||||
- not _skip_cluster | bool
|
||||
|
||||
- name: Check if a reboot is required by APT
|
||||
ansible.builtin.stat:
|
||||
@@ -97,19 +134,30 @@
|
||||
- name: Reboot the node gracefully
|
||||
ansible.builtin.reboot:
|
||||
msg: "Rebooting node to apply new kernel/system updates"
|
||||
reboot_timeout: 600 # Wait up to 10 minutes for the server to return
|
||||
reboot_timeout: 600
|
||||
when: reboot_required_file.stat.exists
|
||||
|
||||
# Explicit barrier: ensure ALL nodes are back online before proceeding to Phase 3.
|
||||
# Without this, any_errors_fatal would abort if one node reboots and another doesn't,
|
||||
# causing them to arrive at Phase 3 at different times.
|
||||
- name: Wait for all nodes to be reachable after (optional) reboot
|
||||
ansible.builtin.wait_for_connection:
|
||||
timeout: 300
|
||||
sleep: 5
|
||||
|
||||
- name: Sync barrier — wait for all nodes before clustering phase
|
||||
ansible.builtin.meta: clear_host_errors
|
||||
|
||||
# ==========================================
|
||||
# PHASE 3: PROXMOX CLUSTERING
|
||||
# ==========================================
|
||||
- name: PROXMOX CLUSTERING (Skipped in Single Node Mode)
|
||||
when: not is_single_node | bool
|
||||
- name: PROXMOX CLUSTERING (Skipped in Single Node / Standalone Mode)
|
||||
when: not _skip_cluster | bool
|
||||
block:
|
||||
- name: Ensure SSH keypair exists for root
|
||||
ansible.builtin.user:
|
||||
name: root
|
||||
generate_ssh_key: yes
|
||||
generate_ssh_key: true
|
||||
ssh_key_bits: 4096
|
||||
|
||||
- name: Fetch public SSH keys from all nodes
|
||||
@@ -124,51 +172,63 @@
|
||||
state: present
|
||||
loop: "{{ ansible_play_hosts }}"
|
||||
|
||||
- name: Compute SSH wildcard host patterns for cluster subnets
|
||||
ansible.builtin.set_fact:
|
||||
_corosync_subnet: "{{ master_corosync_ip | regex_replace('\\.[0-9]+$', '.*') }}"
|
||||
_mgmt_subnet: >-
|
||||
{{ hostvars[groups['proxmox'][0]]['ansible_host']
|
||||
| regex_replace('\\.[0-9]+$', '.*') }}
|
||||
|
||||
- name: Automatically accept host keys for cluster subnets
|
||||
ansible.builtin.blockinfile:
|
||||
path: /root/.ssh/config
|
||||
create: yes
|
||||
create: true
|
||||
mode: '0600'
|
||||
block: |
|
||||
Host {{ master_corosync_ip | regex_replace('\.[0-9]+$', '.*') }} {{ hostvars[groups['proxmox'][0]]['ansible_host'] | regex_replace('\.[0-9]+$', '.*') }}
|
||||
Host {{ _corosync_subnet }} {{ _mgmt_subnet }}
|
||||
StrictHostKeyChecking no
|
||||
UserKnownHostsFile=/dev/null
|
||||
|
||||
- name: Check current Proxmox cluster status
|
||||
ansible.builtin.command: pvecm status
|
||||
register: pvecm_status
|
||||
ignore_errors: yes
|
||||
ignore_errors: true
|
||||
changed_when: false
|
||||
|
||||
- name: Initialize the Proxmox Cluster (Master Node)
|
||||
ansible.builtin.command: pvecm create {{ pve_cluster_name }} --link0 {{ corosync_ip }}
|
||||
when:
|
||||
changed_when: true
|
||||
when:
|
||||
- pvecm_status.rc != 0
|
||||
- is_master | bool
|
||||
|
||||
- name: Wait for Corosync to stabilize
|
||||
ansible.builtin.pause:
|
||||
seconds: 10
|
||||
when:
|
||||
when:
|
||||
- pvecm_status.rc != 0
|
||||
- is_master | bool
|
||||
|
||||
- name: Join the Proxmox Cluster (Worker Nodes)
|
||||
- name: Join the Proxmox Cluster (Worker Nodes — one at a time)
|
||||
ansible.builtin.command: >
|
||||
pvecm add {{ master_corosync_ip }} --link0 {{ corosync_ip }} --use_ssh yes
|
||||
when:
|
||||
register: pvecm_join
|
||||
changed_when: true
|
||||
failed_when: pvecm_join.rc != 0 or 'TASK ERROR' in pvecm_join.stdout or 'TASK ERROR' in pvecm_join.stderr
|
||||
when:
|
||||
- pvecm_status.rc != 0
|
||||
- not is_master | bool
|
||||
async: 60
|
||||
throttle: 1 # Prevent race condition: pvecm add does not support parallel joins
|
||||
async: 120
|
||||
poll: 10
|
||||
|
||||
# ==========================================
|
||||
# PHASE 4: LINSTOR CONFIGURATION
|
||||
# ==========================================
|
||||
- name: LINSTOR CONFIGURATION (Skipped if deploy_linstor=false or Single Node)
|
||||
when:
|
||||
- name: LINSTOR CONFIGURATION (Skipped if deploy_linstor=false or Single Node / Standalone)
|
||||
when:
|
||||
- deploy_linstor | bool
|
||||
- not is_single_node | bool
|
||||
- not _skip_cluster | bool
|
||||
block:
|
||||
- name: Configure Linstor Cluster
|
||||
ansible.builtin.include_role:
|
||||
|
||||
Reference in New Issue
Block a user