Initial commit
This commit is contained in:
61
ansible/roles/linstor_cluster/tasks/main.yml
Normal file
61
ansible/roles/linstor_cluster/tasks/main.yml
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
# tasks file for linstor_cluster
|
||||
|
||||
- name: Safely manage LINSTOR cluster nodes in /etc/hosts
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/hosts
|
||||
marker: "# {mark} ANSIBLE MANAGED BLOCK - LINSTOR CLUSTER"
|
||||
block: |
|
||||
# Linstor Cluster Nodes (Primary: Private Network)
|
||||
{% for host in groups['proxmox'] %}
|
||||
{{ hostvars[host].priv_ip }} {{ host }}
|
||||
{% endfor %}
|
||||
|
||||
# Linstor Cluster Nodes (Fallback: Public Network)
|
||||
{% for host in groups['proxmox'] %}
|
||||
{{ hostvars[host].ansible_host }} {{ host }}-public
|
||||
{% endfor %}
|
||||
|
||||
- name: Ensure /etc/linstor directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/linstor
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Deploy linstor-client.conf
|
||||
ansible.builtin.template:
|
||||
src: linstor-client.conf.j2
|
||||
dest: /etc/linstor/linstor-client.conf
|
||||
mode: '0644'
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# LINSTOR NODE REGISTRATION (Executed ONLY on Node 1)
|
||||
# ---------------------------------------------------------
|
||||
|
||||
- name: Register nodes in LINSTOR
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
if ! linstor node list | grep -q " {{ item }} "; then
|
||||
linstor node create "{{ item }}" "{{ hostvars[item].priv_ip }}" --node-type Combined > /dev/null
|
||||
echo "changed"
|
||||
fi
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: linstor_cluster_node_create
|
||||
changed_when: "'changed' in linstor_cluster_node_create.stdout"
|
||||
loop: "{{ groups['proxmox'] }}"
|
||||
when: inventory_hostname == groups['proxmox'][0]
|
||||
|
||||
- name: Configure Multipath Fallback Interfaces
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
if ! linstor node interface list "{{ item }}" | grep -q " fallback "; then
|
||||
linstor node interface create "{{ item }}" fallback "{{ hostvars[item].ansible_host }}" > /dev/null
|
||||
echo "changed"
|
||||
fi
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: linstor_cluster_int_create
|
||||
changed_when: "'changed' in linstor_cluster_int_create.stdout"
|
||||
loop: "{{ groups['proxmox'] }}"
|
||||
when: inventory_hostname == groups['proxmox'][0]
|
||||
@@ -0,0 +1,3 @@
|
||||
[global]
|
||||
# Ansible dynamically grabs all hosts in the 'proxmox' group and joins them with commas
|
||||
controllers={{ groups['proxmox'] | join(',') }}
|
||||
134
ansible/roles/linstor_ha/tasks/main.yml
Normal file
134
ansible/roles/linstor_ha/tasks/main.yml
Normal file
@@ -0,0 +1,134 @@
|
||||
---
|
||||
# tasks file for linstor_ha
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# STEP 1: CREATE HA RESOURCES (Executed ONLY on Node 1)
|
||||
# ---------------------------------------------------------
|
||||
- name: Create HA Resource Group and Spawn Database Volume
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
if ! linstor resource-group list | grep -q " linstor-db-grp "; then
|
||||
linstor resource-group create --storage-pool "{{ ha_pool }}" --place-count 3 --diskless-on-remaining true linstor-db-grp > /dev/null
|
||||
linstor resource-group drbd-options --auto-promote=no --quorum=majority \
|
||||
--on-suspended-primary-outdated=force-secondary --on-no-quorum=io-error --on-no-data-accessible=io-error linstor-db-grp > /dev/null
|
||||
linstor volume-group create linstor-db-grp > /dev/null
|
||||
linstor resource-group spawn-resources linstor-db-grp linstor_db 200M > /dev/null
|
||||
echo "changed"
|
||||
fi
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: linstor_ha_rg_create
|
||||
changed_when: "'changed' in linstor_ha_rg_create.stdout"
|
||||
when: inventory_hostname == groups['proxmox'][0]
|
||||
|
||||
- name: Wait for DRBD block device to appear locally
|
||||
ansible.builtin.wait_for:
|
||||
path: /dev/drbd/by-res/linstor_db/0
|
||||
state: present
|
||||
timeout: 60
|
||||
when: inventory_hostname == groups['proxmox'][0]
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# STEP 2: PREPARE ALL NODES
|
||||
# ---------------------------------------------------------
|
||||
- name: Install resource-agents
|
||||
ansible.builtin.apt:
|
||||
name: resource-agents
|
||||
state: present
|
||||
|
||||
- name: Ensure local Linstor Controller is stopped and disabled
|
||||
ansible.builtin.systemd:
|
||||
name: linstor-controller
|
||||
enabled: false
|
||||
state: stopped
|
||||
|
||||
- name: Deploy var-lib-linstor.mount unit
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/systemd/system/var-lib-linstor.mount
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Filesystem for the LINSTOR controller
|
||||
|
||||
[Mount]
|
||||
What=/dev/drbd/by-res/linstor_db/0
|
||||
Where=/var/lib/linstor
|
||||
mode: '0644'
|
||||
|
||||
- name: Ensure drbd-reactor directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/drbd-reactor.d
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Deploy DRBD Reactor TOML config
|
||||
ansible.builtin.template:
|
||||
src: linstor_db.toml.j2
|
||||
dest: /etc/drbd-reactor.d/linstor_db.toml
|
||||
mode: '0644'
|
||||
|
||||
# Notice how the stat task is gone, replaced by 'creates' and 'removes'
|
||||
- name: Move existing local database to .orig safely
|
||||
ansible.builtin.command: mv /var/lib/linstor /var/lib/linstor.orig
|
||||
args:
|
||||
creates: /var/lib/linstor.orig
|
||||
removes: /var/lib/linstor
|
||||
|
||||
- name: Create empty mount point and set immutable flag (+i)
|
||||
ansible.builtin.file:
|
||||
path: /var/lib/linstor
|
||||
state: directory
|
||||
mode: '0755'
|
||||
attributes: '+i'
|
||||
|
||||
- name: Ensure Satellite Override directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/systemd/system/linstor-satellite.service.d
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Deploy Satellite Keep-Resource Override
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/systemd/system/linstor-satellite.service.d/override.conf
|
||||
content: |
|
||||
[Service]
|
||||
Environment=LS_KEEP_RES=linstor_db
|
||||
mode: '0644'
|
||||
|
||||
- name: Reload daemon and restart Satellite
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
name: linstor-satellite
|
||||
state: restarted
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# STEP 3: FORMAT & MIGRATE DB (Executed ONLY on Node 1)
|
||||
# ---------------------------------------------------------
|
||||
- name: Format HA Volume and Migrate Database
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
if ! blkid /dev/drbd/by-res/linstor_db/0 | grep -q "ext4"; then
|
||||
drbdadm primary linstor_db
|
||||
mkfs.ext4 /dev/drbd/by-res/linstor_db/0 > /dev/null
|
||||
|
||||
chattr -i /var/lib/linstor
|
||||
mount /dev/drbd/by-res/linstor_db/0 /var/lib/linstor
|
||||
cp -a /var/lib/linstor.orig/* /var/lib/linstor/
|
||||
umount /var/lib/linstor
|
||||
chattr +i /var/lib/linstor
|
||||
drbdadm secondary linstor_db
|
||||
echo "changed"
|
||||
fi
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: linstor_ha_migrate
|
||||
changed_when: "'changed' in linstor_ha_migrate.stdout"
|
||||
when: inventory_hostname == groups['proxmox'][0]
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# STEP 4: ACTIVATE HA REACTOR
|
||||
# ---------------------------------------------------------
|
||||
- name: Enable and start DRBD Reactor
|
||||
ansible.builtin.systemd:
|
||||
name: drbd-reactor
|
||||
enabled: true
|
||||
state: started
|
||||
9
ansible/roles/linstor_ha/templates/linstor_db.toml.j2
Normal file
9
ansible/roles/linstor_ha/templates/linstor_db.toml.j2
Normal file
@@ -0,0 +1,9 @@
|
||||
[[promoter]]
|
||||
[promoter.resources.linstor_db]
|
||||
start = [
|
||||
"ocf:heartbeat:IPaddr2 service_ip cidr_netmask={{ ha_vip_cidr }} ip={{ ha_vip }}",
|
||||
"var-lib-linstor.mount",
|
||||
"linstor-controller.service"
|
||||
]
|
||||
on-drbd-demote-failure = "reboot"
|
||||
secondary-force = true
|
||||
63
ansible/roles/linstor_install/tasks/main.yml
Normal file
63
ansible/roles/linstor_install/tasks/main.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
# tasks file for linstor_install
|
||||
|
||||
- name: Install Proxmox kernel headers
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "proxmox-headers-{{ ansible_facts['kernel'] }}"
|
||||
- proxmox-default-headers
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Install LINSTOR, DRBD, and LVM dependencies
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- drbd-dkms
|
||||
- drbd-utils
|
||||
- lvm2
|
||||
- thin-provisioning-tools
|
||||
- linstor-satellite
|
||||
- linstor-proxmox
|
||||
- linstor-controller
|
||||
- linstor-client
|
||||
- drbd-reactor
|
||||
state: present
|
||||
|
||||
- name: Check currently loaded DRBD version
|
||||
ansible.builtin.shell: cat /proc/drbd || true
|
||||
register: linstor_install_drbd_version
|
||||
changed_when: false
|
||||
|
||||
- name: Eject legacy DRBD 8.4 module if present
|
||||
community.general.modprobe:
|
||||
name: drbd
|
||||
state: absent
|
||||
when: "'version: 8' in linstor_install_drbd_version.stdout"
|
||||
|
||||
- name: Ensure DRBD kernel module is loaded
|
||||
community.general.modprobe:
|
||||
name: drbd
|
||||
state: present
|
||||
|
||||
- name: Enable and start base services (Satellite & Reactor)
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
enabled: true
|
||||
state: started
|
||||
loop:
|
||||
- linstor-satellite
|
||||
- drbd-reactor
|
||||
|
||||
- name: Ensure LINSTOR Controller is disabled safely on all nodes
|
||||
ansible.builtin.systemd:
|
||||
name: linstor-controller
|
||||
enabled: false
|
||||
state: stopped
|
||||
|
||||
- name: Enable and start LINSTOR Controller strictly on Node 1
|
||||
ansible.builtin.systemd:
|
||||
name: linstor-controller
|
||||
enabled: true
|
||||
state: started
|
||||
# Ansible Magic: This dynamically targets the very first node listed in your inventory under the 'proxmox' group!
|
||||
when: inventory_hostname == groups['proxmox'][0]
|
||||
24
ansible/roles/linstor_repo/tasks/main.yml
Normal file
24
ansible/roles/linstor_repo/tasks/main.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
# tasks file for linstor_repo
|
||||
|
||||
- name: Download LINBIT keyring package
|
||||
ansible.builtin.get_url:
|
||||
url: https://packages.linbit.com/public/linbit-keyring.deb
|
||||
dest: /tmp/linbit-keyring.deb
|
||||
mode: '0644'
|
||||
|
||||
- name: Install LINBIT keyring
|
||||
ansible.builtin.apt:
|
||||
deb: /tmp/linbit-keyring.deb
|
||||
state: present
|
||||
|
||||
- name: Add LINBIT APT repository for Proxmox
|
||||
ansible.builtin.apt_repository:
|
||||
repo: >-
|
||||
deb [signed-by=/etc/apt/trusted.gpg.d/linbit-keyring.gpg]
|
||||
https://packages.linbit.com/public/
|
||||
proxmox-{{ ansible_facts['distribution_major_version'] | int - 4 }}
|
||||
drbd-9
|
||||
filename: linbit
|
||||
state: present
|
||||
update_cache: true
|
||||
69
ansible/roles/linstor_storage/tasks/main.yml
Normal file
69
ansible/roles/linstor_storage/tasks/main.yml
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
# tasks file for linstor_storage
|
||||
|
||||
- name: Configure LVM global_filter for DRBD safely
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/lvm/lvm.conf
|
||||
insertafter: '^[ \t]*devices[ \t]*\{'
|
||||
# This regex ensures we only ever have ONE DRBD filter line, replacing the old one if it exists
|
||||
regexp: '^[ \t]*global_filter[ \t]*=.*r\|\^/dev/drbd\|'
|
||||
line: ' global_filter = [ "r|^/dev/drbd|", "r|^/dev/mapper/[lL]instor|" ]'
|
||||
state: present
|
||||
|
||||
- name: Ensure Volume Groups exist for raw disks
|
||||
community.general.lvg:
|
||||
vg: "{{ item.vg_name }}"
|
||||
pvs: "{{ item.targets[inventory_hostname] }}"
|
||||
loop: "{{ linstor_storage_pools }}"
|
||||
# Jinja2 magic: Only run this LVM task if the target string starts with '/dev/'
|
||||
when: item.targets[inventory_hostname].startswith('/dev/')
|
||||
|
||||
- name: Ensure LVM Thin Pools exist for raw disks
|
||||
community.general.lvol:
|
||||
vg: "{{ item.vg_name }}"
|
||||
lv: "{{ item.thin_name }}"
|
||||
size: "100%FREE"
|
||||
opts: "-T -Zn"
|
||||
shrink: false
|
||||
loop: "{{ linstor_storage_pools }}"
|
||||
when: item.targets[inventory_hostname].startswith('/dev/')
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# LINSTOR STORAGE REGISTRATION (Executed ONLY on Node 1)
|
||||
# ---------------------------------------------------------
|
||||
|
||||
- name: Register Storage Pools in LINSTOR
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
|
||||
# We use Jinja2 to write a custom bash loop for exactly the nodes in our inventory
|
||||
{% for host in groups['proxmox'] %}
|
||||
{% set target = item.targets[host] %}
|
||||
{% set lvm_path = (item.vg_name + '/' + item.thin_name) if target.startswith('/dev/') else target %}
|
||||
|
||||
if ! linstor storage-pool list | grep -q " {{ host }} .* {{ item.pool_name }} "; then
|
||||
linstor storage-pool create lvmthin "{{ host }}" "{{ item.pool_name }}" "{{ lvm_path }}" > /dev/null
|
||||
echo "changed"
|
||||
fi
|
||||
{% endfor %}
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: linstor_storage_pool_create
|
||||
changed_when: "'changed' in linstor_storage_pool_create.stdout"
|
||||
loop: "{{ linstor_storage_pools }}"
|
||||
when: inventory_hostname == groups['proxmox'][0]
|
||||
|
||||
- name: Create Resource Groups and Volume Groups
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
if ! linstor resource-group list | grep -q " {{ item.rg_name }} "; then
|
||||
linstor resource-group create "{{ item.rg_name }}" --storage-pool "{{ item.pool_name }}" --place-count 3 > /dev/null
|
||||
linstor volume-group create "{{ item.rg_name }}" > /dev/null
|
||||
echo "changed"
|
||||
fi
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: linstor_storage_rg_create
|
||||
changed_when: "'changed' in linstor_storage_rg_create.stdout"
|
||||
loop: "{{ linstor_storage_pools }}"
|
||||
when: inventory_hostname == groups['proxmox'][0]
|
||||
29
ansible/roles/proxmox_storage/tasks/main.yml
Normal file
29
ansible/roles/proxmox_storage/tasks/main.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
# tasks file for proxmox_storage
|
||||
|
||||
- name: Create a safety backup of Proxmox storage.cfg
|
||||
ansible.builtin.copy:
|
||||
src: /etc/pve/storage.cfg
|
||||
dest: "/root/pve_storage.cfg.bak.{{ ansible_facts['date_time']['epoch'] }}"
|
||||
remote_src: true
|
||||
mode: '0600'
|
||||
when: inventory_hostname == groups['proxmox'][0]
|
||||
|
||||
- name: Register LINSTOR resource groups in Proxmox GUI (pvesm)
|
||||
ansible.builtin.shell: |
|
||||
set -o pipefail
|
||||
|
||||
# We use the ha_vip variable, but gracefully fallback to Node 1's public IP if it's undefined
|
||||
CONTROLLER="{{ ha_vip | default(hostvars[groups['proxmox'][0]].ansible_host) }}"
|
||||
STORAGE_ID="ls-{{ item.rg_name }}"
|
||||
|
||||
if ! pvesm status | grep -q "^${STORAGE_ID} "; then
|
||||
pvesm add drbd "${STORAGE_ID}" --controller "${CONTROLLER}" --resourcegroup "{{ item.rg_name }}" --content images,rootdir > /dev/null
|
||||
echo "changed"
|
||||
fi
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: proxmox_storage_pvesm_add
|
||||
changed_when: "'changed' in proxmox_storage_pvesm_add.stdout"
|
||||
loop: "{{ linstor_storage_pools }}"
|
||||
when: inventory_hostname == groups['proxmox'][0]
|
||||
Reference in New Issue
Block a user