2

Original question:

I want to roll out a partitioning layout for a home lab setup of GlusterFS and also want to do it using Ansible so I can re-use the configuration for a more stable setup later on. This is also the first time I work with Ansible so please bear with me ;)

How do I use variables across roles correctly?

I created a role for the basic partitioning of a single data disk and I want to have 3 disks on each node.

My nodes are as follows (path = inventories/staging/hosts.yaml)

all:
  hosts:
    node1:
      ansible_host: node1
    node2:
      ansible_host: node2

storageservers:
  hosts:
    node1:
      ansible_host: node1
    node2:
      ansible_host: node2

The base role used by the aggregating role is as follows (path = roles/gluster-node-partition/tasks/main.yml):

---
# tasks file for gluster-node-partition
- name: 'Create {{ partition_name }} partition'
  parted:
    device: '{{ device_name }}'
    number: 1
    label: gpt
    name: '{{ partition_name }}'
    state: present
  become: true

- name: 'Create filesystem on {{ partition_name }} partition'
  filesystem:
    fstype: btrfs
    dev: '{{ device_name }}1' #should expand to something like '/dev/sdb1'
  become: true

- name: 'Create mount point for {{ partition_name }} partition'
  file:
    path: '/{{ partition_name }}'
    state: directory
  become: true

- name: 'Mount {{ partition_name }} partition'
  mount:
    path: '/{{ partition_name }}'
    src: 'LABEL={{ partition_name }}'
    fstype: btrfs
    state: present
  become: true

The aggregating role is as follows (path = roles/gluster-node/tasks/main.yml):

---
# tasks file for gluster-node
- include_role:
    name: gluster-node-partition

- name: Create data1 partition
  include_role:
    name: gluster-node-partition
  vars:
    device_name: '/dev/sdb'
    partition_name: 'data1'

- name: Create data2 partition
  include_role:
    name: gluster-node-partition
  vars:
    device_name: '/dev/sdc'
    partition_name: 'data2'

- name: Create data3 partition
  include_role:
    name: cluster-node-partition
  vars:
    device_name: '/dev/sdd'
    partition_name: 'data3'

I invoke the following command ansible-playbook -i inventories/playground/hosts.yaml site.yml -K

The output is as follows

BECOME password: 

PLAY [storageservers] *****************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************
ok: [node2]
ok: [node1]

TASK [include_role : gluster-node-partition] ******************************************************************************************************************

TASK [gluster-node-partition : Create {{ partition_name }} partition] *****************************************************************************************
fatal: [node1]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'device_name' is undefined\n\nThe error appears to be in '/home/user/ansible/roles/gluster-node-partition/tasks/main.yml': line 3, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n# tasks file for gluster-node-partition\n- name: 'Create {{ partition_name }} partition'\n  ^ here\nWe could be wrong, but this one looks like it might be an issue with\nmissing quotes. Always quote template expression brackets when they\nstart a value. For instance:\n\n    with_items:\n      - {{ foo }}\n\nShould be written as:\n\n    with_items:\n      - \"{{ foo }}\"\n"}
fatal: [node2]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'device_name' is undefined\n\nThe error appears to be in '/home/user/ansible/roles/gluster-node-partition/tasks/main.yml': line 3, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n# tasks file for gluster-node-partition\n- name: 'Create {{ partition_name }} partition'\n  ^ here\nWe could be wrong, but this one looks like it might be an issue with\nmissing quotes. Always quote template expression brackets when they\nstart a value. For instance:\n\n    with_items:\n      - {{ foo }}\n\nShould be written as:\n\n    with_items:\n      - \"{{ foo }}\"\n"}

PLAY RECAP ****************************************************************************************************************************************************
node1                      : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
node2                      : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0  

Solution by w00te: Thanks to w00te, it works now :) added the vars like this (roles/gluster-node/tasks/main.yml)

# tasks file for gluster-node
- include_role:
    name: gluster-node-partition
  vars:
    device_name: '/dev/sdb'
    partition_name: 'data1'
1

Your very first role include does not have variables provided with it, so it will fail.

Here's an excerpt from above showing this. Notice lines 1-2 have no variables after them (unlike the include following that which does correctly provide the variables).

- include_role:
    name: gluster-node-partition

- name: Create data1 partition
  include_role:
    name: gluster-node-partition
  vars:
    device_name: '/dev/sdb'
    partition_name: 'data1'

As roles/gluster-node-partition/tasks/main.yml uses device_name twice, and it is not defined for that first call, it will fail. The two usages are noted on line 2 and on the last line below.

  parted:
    device: '{{ device_name }}'
    number: 1
    label: gpt
    name: '{{ partition_name }}'
    state: present
  become: true

- name: 'Create filesystem on {{ partition_name }} partition'
  filesystem:
    fstype: btrfs
    dev: '{{ device_name }}1' #should expand to something like '/dev/sdb1'
| improve this answer | |
  • Thanks, it works now :) added the solution to the first post – Christian Mar 30 at 7:39
1

You might want to use the group vars feature, so that your playbooks stay clean and re-usable. You define a storageservers group in your inventory; associated with this group, Ansible will look for group_vars/storageservers.yml for the vars to associate with this group. Here, you can set the variables:

# group_vars/storageservers.yml
---
partitions:
 - device_name: /dev/sdb
   partition_name: /data1
 - device_name: /dev/sdc
   partition_name: /data2
 - device_name: /dev/sdd
   partition_name: /data3

Now, you have a list of partitions which will be picked up when you target hosts: storageservers, which you can loop over with one statement instead of 3:

# playbook.yml
---
hosts: storage
become: true
roles:
  - gluster-node-partition

Updated tasks:

# roles/gluster-node-partition/tasks.yml
- name: "Create {{ item['partition_name'] }} partition"
  parted:
    device: "{{ item['device_name'] }}"
    number: 1
    label: gpt
    name: "{{ item['partition_name' }}"
    state: present
  loop: "{{ partitions }}"

- name: "Create filesystem on {{ item['partition_name'] }} partition"
  filesystem:
    fstype: btrfs
    dev: "{{ item['device_name'] }}1" #should expand to something like '/dev/sdb1'
  item: "{{ partitions }}"

- name: "Create mount point for {{ item['partition_name'] }} partition"
  file:
    path: '/{{ item['partition_name'] }}'
    state: directory
  loop: "{{ partitions }}"

- name: "Mount {{ item['partition_name'] }} partition"
  mount:
    path: "/{{ item['partition_name'] }}"
    src: "LABEL={{ item['partition_name'] }}"
    fstype: btrfs
    state: present
  loop: "{{ partitions }}"

Doing it this way separates variables from tasks. If you want to add or change a filesystem, you only need to change the variable and not the playbook.

You also have a much cleaner playbook -- just a few lines. It's clear that this all of the filesystem stuff is contained in the role.

If you have a different set of machines where you want different mounts, you only have to add a new group, give that group a new partitions variable, and target it in the same way by setting hosts: other_storage_servers.

For reference, see the Ansible docs on :

| improve this answer | |
  • Thanks a lot for the suggestion :) – Christian Mar 30 at 7:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.