--- - 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