400 Configure ZFS

Use case

Use the role vbotka.freebsd.zfs to configure ZFS pools and datasets.

Tree

shell > tree .
.
├── ansible.cfg
├── host_vars
│   └── iocage_04
│       ├── loader.yml
│       └── zfs.yml
├── iocage.ini
├── pb-loader.yml
└── pb-zfs.yml

Synopsis

  • At the managed host iocage_04

    • create ZFS pools:

      • zroot

      • iocage

    • create and mount ZFS datasets:

      • zroot/export mount /export

      • iocage/ports mount /usr/ports

      • iocage/src mount /usr/src

      • iocage/obj mount /usr/obj

      • iocage/poudriere mount /usr/local/poudriere

Requirements

Notes

The role vbotka.freebsd.postinstall is used to configure /boot/loader.conf

Note

vbotka.freebsd.zfs is the role zfs in the collection vbotka.freebsd.
vbotka.freebsd_zfs is the role freebsd_zfs in the namespace vbotka.
Please make sure the versions are the same before you switch between them.

Known issues

The module community.general.zpool can’t create correct diff. For example,

(Pdb) p vdev_layout_diff
{'before': {'vdevs': [{'type': 'stripe', 'disks': ['/dev/ada2']}, {'type': 'stripe', 'disks': ['/dev/ada3']}]},
 'after': {'vdevs': [{'type': 'stripe', 'disks': ['/dev/ada2', '/dev/ada3']}]}}

This makes the module not idempotent. It crashes when running repeatedly. For example,

failed: [srv.example.org] (item=iocage) =>
    ansible_loop_var: item
    changed: false
    cmd: /sbin/zpool add iocage /dev/ada2 /dev/ada3
    item:
        key: iocage
        value:
            vdevs:
            -   disks:
                - /dev/ada2
                - /dev/ada3
    msg: |-
        invalid vdev specification
        use '-f' to override the following errors:
        /dev/ada2 is part of active pool 'iocage'
        /dev/ada3 is part of active pool 'iocage'
    rc: 1
    ...

Setting force: true doesn’t help. At the moment, the only workaround is to skip the module if the pool already exists. You’ll see a warning. For example,

TASK [vbotka.freebsd_zfs : Pools: WARNING | community.general.zpool skipped.] ****
ok: [srv.example.org] =>
    msg: |-
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # WARNING:
        #
        # The module community.general.zpool is not idempotent. It crashes
        # when running repeatedly. Because of the poor quality, the module
        # community.general.zpool will be skipped for pools:
        # ['iocage']
        #
        # Configure the skipped pools manually, if necessary.
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

ansible.cfg

[defaults]
gathering = explicit
callback_result_format = yaml
display_skipped_hosts = false

[connection]
pipelining = true

Inventory iocage.ini

iocage_04 ansible_host=10.1.0.29

[iocage]
iocage_04

[iocage:vars]
ansible_user=admin
ansible_become=true
ansible_python_interpreter=auto_silent

host_vars

host_vars/iocage_04/loader.yml
fp_loader_conf_template: ''
fp_loader_conf:
  - name: vfs.zfs.prefetch.disable
    value: "0"
host_vars/iocage_04/zfs.yml
fzfs_enable: true

fzfs_pools:
  zroot:
    vdevs:
      - disks: [/dev/ada0p4, /dev/ada1p4]
  iocage:
    vdevs:
      - disks: [/dev/ada2, /dev/ada3]

fzfs_manage:
  - name: zroot/export
    state: present
    extra_zfs_properties:
      compression: 'on'
      mountpoint: /export
  - name: iocage/ports
    state: present
    extra_zfs_properties:
      compression: 'on'
      mountpoint: /usr/ports
  - name: iocage/src
    state: present
    extra_zfs_properties:
      compression: 'on'
      mountpoint: /usr/src
  - name: iocage/obj
    state: present
    extra_zfs_properties:
      compression: 'on'
      mountpoint: /usr/obj
  - name: iocage/poudriere
    state: present
    extra_zfs_properties:
      compression: 'on'
      mountpoint: /usr/local/poudriere

Playbook pb-loader.yml

- name: Configure loader.conf
  hosts: iocage
  gather_facts: true

  tasks:

    - name: Configure loader.conf
      ansible.builtin.import_role:
        name: vbotka.freebsd.postinstall
        tasks_from: loader

Playbook output - loader.conf

(env) > ansible-playbook pb-loader.yml -i iocage.ini
PLAY [Configure loader.conf] ***************************************************

TASK [Gathering Facts] *********************************************************
ok: [iocage_04]

TASK [vbotka.freebsd.postinstall : Loader: Backup orig /boot/loader.conf] ******
ok: [iocage_04]

TASK [vbotka.freebsd.postinstall : Loader: Configure by sysctl /boot/loader.conf] ***
ok: [iocage_04] => (item={'name': 'vfs.zfs.prefetch.disable', 'value': '0'})

TASK [vbotka.freebsd.postinstall : Loader: Get sysctls settable via loader.] ***
ok: [iocage_04]

PLAY RECAP *********************************************************************
iocage_04                  : ok=4    changed=0    unreachable=0    failed=0    skipped=5    rescued=0    ignored=0   

Note

Reboot if you see the message

[MESSAGE] Reboot to activate configuration in /boot/loader.conf

Playbook pb-zfs.yml

- name: Configure ZFS.
  hosts: iocage
  gather_facts: true

  roles:
    - vbotka.freebsd.zfs

Playbook output - Display variables

(env) > ansible-playbook pb-zfs.yml -i iocage.ini -t fzfs_debug -e fzfs_debug=true
PLAY [Configure ZFS.] **********************************************************

TASK [Gathering Facts] *********************************************************
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Debug fzfs_debug=true] ******************************
ok: [iocage_04] => 
    msg: |-
        fzfs_role_version: 2.8.2
        ansible_role_name: vbotka.freebsd.zfs
        ansible_facts.architecture: amd64
        ansible_facts.os_family: FreeBSD
        ansible_facts.distribution: FreeBSD
        ansible_facts.distribution_major_version: 15
        ansible_facts.distribution_version: 15.0
        ansible_facts.distribution_release: 15.0-RELEASE-p1
        ansible_facts.python_version: 3.11.14

        fzfs_enable: True
        fzfs_debug2: False
        fzfs_pools_sanity: True
        fzfs_assert_quiet: True
        fzfs_facts_ds: False
        fzfs_facts_recurse: True
        fzfs_mount_all: True
        fzfs_sysctl_tuneables_warning: "True"

        fzfs_sysctl_conf:
          []

        fzfs_rcconf:
          []

        fzfs_pools:
          iocage:
            vdevs:
            - disks: [/dev/ada2, /dev/ada3]
          zroot:
            vdevs:
            - disks: [/dev/ada0p4, /dev/ada1p4]

        fzfs_manage:
          - extra_zfs_properties: {compression: 'on', mountpoint: /export}
            name: zroot/export
            state: present
          - extra_zfs_properties: {compression: 'on', mountpoint: /usr/ports}
            name: iocage/ports
            state: present
          - extra_zfs_properties: {compression: 'on', mountpoint: /usr/src}
            name: iocage/src
            state: present
          - extra_zfs_properties: {compression: 'on', mountpoint: /usr/obj}
            name: iocage/obj
            state: present
          - extra_zfs_properties: {compression: 'on', mountpoint: /usr/local/poudriere}
            name: iocage/poudriere
            state: present

        fzfs_mountpoints:
          []

PLAY RECAP *********************************************************************
iocage_04                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Playbook output - Configure ZFS

(env) > ansible-playbook pb-zfs.yml -i iocage.ini
PLAY [Configure ZFS.] **********************************************************

TASK [Gathering Facts] *********************************************************
ok: [iocage_04]

TASK [Sysctl: Include vbotka.freebsd.postinstall sysctl] ***********************
included: vbotka.freebsd.postinstall for iocage_04

TASK [vbotka.freebsd.postinstall : Sysctl: Get sysctls settable via loader.] ***
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Rcconf: Enable and Start zfs] ***********************
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Pools: Get zpool status.] ***************************
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Pools: Sanity: No fzfs_pools disks already in other pools.] ***
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Pools: Sanity: No fzfs_pools disks already mounted.] ***
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Pools: Sanity: fzfs_pools disks are unique.] ********
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Pools: WARNING | community.general.zpool skipped.] ***
ok: [iocage_04] => 
    msg: |-
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # WARNING:
        #
        # The module community.general.zpool is not idempotent. It crashes
        # when running repeatedly. Because of the poor quality, the module
        # community.general.zpool will be skipped for pools:
        # ['zroot', 'iocage']
        #
        # Configure the skipped pools manually, if necessary.
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

TASK [vbotka.freebsd.zfs : Facts: Run zpool_facts] *****************************
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Manage: Sanity all pools available.] ****************
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Manage: Manage zfs] *********************************
ok: [iocage_04] => (item=zroot/export)
ok: [iocage_04] => (item=iocage/ports)
ok: [iocage_04] => (item=iocage/src)
ok: [iocage_04] => (item=iocage/obj)
ok: [iocage_04] => (item=iocage/poudriere)

PLAY RECAP *********************************************************************
iocage_04                  : ok=12   changed=0    unreachable=0    failed=0    skipped=15   rescued=0    ignored=0   

Playbook output - List pools

(env) > ansible-playbook pb-zfs.yml -t fzfs_facts_pools -e fzfs_debug=true
PLAY [Configure ZFS.] **********************************************************

TASK [Gathering Facts] *********************************************************
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Facts: Run zpool_facts] *****************************
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Facts: Debug list pools fzfs_debug=true] ************
ok: [iocage_04] => 
    msg: |-
        fzfs_pools_required: ['iocage', 'zroot']
        fzfs_pools_missing: []
        fzfs_pools_present: ['iocage', 'zroot']
        name: iocage size: 7.25T free: 7.24T
        name: zroot size: 472G free: 465G

PLAY RECAP *********************************************************************
iocage_04                  : ok=3    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

Playbook output - List datasets

(env) > ansible-playbook pb-zfs.yml -t fzfs_facts_ds -e fzfs_facts_ds=true -e fzfs_debug=true
PLAY [Configure ZFS.] **********************************************************

TASK [Gathering Facts] *********************************************************
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Facts: Run zpool_facts] *****************************
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Facts: Run zfs_facts] *******************************
ok: [iocage_04] => (item=iocage)
ok: [iocage_04] => (item=zroot)

TASK [vbotka.freebsd.zfs : Facts: Get dictionary of datasets] ******************
ok: [iocage_04]

TASK [vbotka.freebsd.zfs : Facts: Debug list of datasets fzfs_debug=true] ******
ok: [iocage_04] => 
    fzfs_datasets | to_nice_yaml: |-
        iocage:
            iocage: /iocage
            iocage/iocage: /iocage/iocage
            iocage/iocage/download: /iocage/iocage/download
            iocage/iocage/download/14.3-RELEASE: /iocage/iocage/download/14.3-RELEASE
            iocage/iocage/download/15.0-RELEASE: /iocage/iocage/download/15.0-RELEASE
            iocage/iocage/images: /iocage/iocage/images
            iocage/iocage/jails: /iocage/iocage/jails
            iocage/iocage/log: /iocage/iocage/log
            iocage/iocage/releases: /iocage/iocage/releases
            iocage/iocage/releases/14.3-RELEASE: /iocage/iocage/releases/14.3-RELEASE
            iocage/iocage/releases/14.3-RELEASE/root: /iocage/iocage/releases/14.3-RELEASE/root
            iocage/iocage/releases/15.0-RELEASE: /iocage/iocage/releases/15.0-RELEASE
            iocage/iocage/releases/15.0-RELEASE/root: /iocage/iocage/releases/15.0-RELEASE/root
            iocage/iocage/releases/15.0-RELEASE/root@ansible_client: null
            iocage/iocage/releases/15.0-RELEASE/root@ansible_client_apache: null
            iocage/iocage/releases/15.0-RELEASE/root@ansible_client_pull: null
            iocage/iocage/templates: /iocage/iocage/templates
            iocage/iocage/templates/ansible_client: /iocage/iocage/templates/ansible_client
            iocage/iocage/templates/ansible_client/root: /iocage/iocage/templates/ansible_client/root
            iocage/iocage/templates/ansible_client_apache: /iocage/iocage/templates/ansible_client_apache
            iocage/iocage/templates/ansible_client_apache/root: /iocage/iocage/templates/ansible_client_apache/root
            iocage/iocage/templates/ansible_client_pull: /iocage/iocage/templates/ansible_client_pull
            iocage/iocage/templates/ansible_client_pull/root: /iocage/iocage/templates/ansible_client_pull/root
            iocage/obj: /usr/obj
            iocage/ports: /usr/ports
            iocage/poudriere: /usr/local/poudriere
            iocage/src: /usr/src
        zroot:
            zroot: /zroot
            zroot/ROOT: none
            zroot/ROOT/default: /
            zroot/export: /export
            zroot/home: /home
            zroot/home/admin: /home/admin
            zroot/home/asadmin: /home/asadmin
            zroot/tmp: /tmp
            zroot/usr: /usr
            zroot/usr/ports: /usr/ports
            zroot/usr/src: /usr/src
            zroot/var: /var
            zroot/var/audit: /var/audit
            zroot/var/crash: /var/crash
            zroot/var/log: /var/log
            zroot/var/mail: /var/mail
            zroot/var/tmp: /var/tmp

PLAY RECAP *********************************************************************
iocage_04                  : ok=5    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0