platform/platform-infra/ansible/roles/zfs/tasks/main.yml
Bart Van Geyt 7b15fa9bca feat(ansible/zfs): support file-backed pool (+ single/mirror modes)
Add zfs_pool_mode: file|single|mirror.
- file (default): loopback disk image at zfs_pool_file_path — real ZFS with
  no spare disk, ideal for a cost-optimized test VM; wipes nothing.
- single/mirror: whole spare disk(s); mirror for production redundancy.
Guard/probe now loops over zfs_pool_disks. Update group_vars, README (modes,
prerequisites, safety), and CLAUDE.md current-focus note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-09 03:09:38 +02:00

92 lines
3.4 KiB
YAML

---
- name: Install ZFS userland + kernel module
ansible.builtin.apt:
name: zfsutils-linux
state: present
- name: Ensure the ZFS kernel module is loaded
community.general.modprobe:
name: zfs
state: present
# --- Pool creation (guarded & destructive) ----------------------------------
- name: Check whether the ZFS pool already exists
ansible.builtin.command: "zpool list -H -o name {{ zfs_pool_name }}"
register: zpool_check
changed_when: false
failed_when: false
- name: Record pool existence
ansible.builtin.set_fact:
zpool_exists: "{{ zpool_check.rc == 0 }}"
# --- File mode: loopback disk image (no spare disk needed) ------------------
- name: Ensure the backing-file directory exists (file mode)
ansible.builtin.file:
path: "{{ zfs_pool_file_path | dirname }}"
state: directory
owner: root
group: root
mode: "0700"
when: not zpool_exists and zfs_pool_mode == 'file'
- name: Create the sparse backing file (file mode)
ansible.builtin.command: "truncate -s {{ zfs_pool_file_size }} {{ zfs_pool_file_path }}"
args:
creates: "{{ zfs_pool_file_path }}"
when: not zpool_exists and zfs_pool_mode == 'file'
- name: Create the ZFS pool on the backing file (file mode)
ansible.builtin.command: >-
zpool create -o ashift=12
-O compression={{ zfs_compression }}
-O atime=off -O xattr=sa -O acltype=posixacl
-O mountpoint=/{{ zfs_pool_name }}
{{ zfs_pool_name }} {{ zfs_pool_file_path }}
when: not zpool_exists and zfs_pool_mode == 'file'
# --- Disk modes: single / mirror (guarded & destructive) --------------------
- name: Probe the target disks for existing data (disk modes)
ansible.builtin.command: "lsblk -nro FSTYPE,MOUNTPOINT,PARTTYPE {{ item }}"
register: disk_probes
changed_when: false
loop: "{{ zfs_pool_disks }}"
when: not zpool_exists and zfs_pool_mode in ['single', 'mirror']
- name: Refuse to create a pool on a non-empty disk unless forced
ansible.builtin.assert:
that:
- (item.stdout | trim | length == 0) or zfs_pool_force
fail_msg: >-
{{ item.item }} appears to already contain data ({{ item.stdout | trim }}).
Refusing to create the pool. Verify the device, then set zfs_pool_force=true.
loop: "{{ disk_probes.results | default([]) }}"
loop_control:
label: "{{ item.item | default('') }}"
when: not zpool_exists and zfs_pool_mode in ['single', 'mirror']
- name: Create the ZFS pool on the dedicated disk(s) (disk modes)
ansible.builtin.command: >-
zpool create {{ '-f ' if zfs_pool_force else '' }}-o ashift=12
-O compression={{ zfs_compression }}
-O atime=off -O xattr=sa -O acltype=posixacl
-O mountpoint=/{{ zfs_pool_name }}
{{ zfs_pool_name }}
{{ 'mirror ' if zfs_pool_mode == 'mirror' else '' }}{{ zfs_pool_disks | join(' ') }}
when: not zpool_exists and zfs_pool_mode in ['single', 'mirror']
# --- Datasets ---------------------------------------------------------------
- name: List existing datasets
ansible.builtin.command: "zfs list -H -o name"
register: zfs_existing
changed_when: false
- name: Create platform + customers datasets
ansible.builtin.command: >-
zfs create -p
{% if item.mountpoint is defined %}-o mountpoint={{ item.mountpoint }}{% endif %}
{{ zfs_pool_name }}/{{ item.path }}
loop: "{{ zfs_child_datasets }}"
loop_control:
label: "{{ zfs_pool_name }}/{{ item.path }}"
when: (zfs_pool_name ~ '/' ~ item.path) not in zfs_existing.stdout_lines