Shubham Jairath
Shubham Jairath

Reputation: 392

How to mount multiple disks in a loop using ansible

I just started working with Ansible recently. I am trying to mount 4 disks on azure VM which were attached using terraform. Each disk was passed with a LUN number and I am fetching the device name(sdc,sdd etc.) for each disk using that LUN no.and grep.

    - name: get volume name
      shell: echo "/dev/$(ls -l /dev/disk/azure/scsi1 |grep {{ item.lun }}|egrep -o "([^\/]+$)")"
      register: volumename
    - parted:
        device: "{{ volumename.stdout }}"
        number: 1
        state: present
    - filesystem:
        fstype: xfs
        dev: "{{ volumename.stdout }}"
    - mount:
        fstype: xfs
        opts: noatime
        src: "{{ volumename.stdout }}"
        path: "{{ item.mountpoint }}"
        state: mounted
    - command: blkid -s UUID -o value {{ volumename.stdout }}
      register: volumename_disk

    - blockinfile:
        path: /etc/fstab
        state: present
        block: |
          UUID={{ volumename_disk.stdout }}   {{ volumename.stdout }}      xfs defaults,noatime,nofail 0 0

      loop:
        - { lun: 'lun0', mountpoint: '/apps/mysql/binlog', diskname: 'binlog' }
        - { lun: 'lun1', mountpoint: '/apps/mysql/innodb', diskname: 'innodb' }
        - { lun: 'lun2', mountpoint: '/apps/mysql/data', diskname: 'data' }
        - { lun: 'lun3', mountpoint: '/apps/mysql', diskname: 'backup' }
      tags: test

I keep getting volumename variable is not defined error. Is there any way to set volumename as global so that the next playbook can access it or How I can improve this code ?

Upvotes: 2

Views: 3581

Answers (3)

WEBjuju
WEBjuju

Reputation: 6581

Everything here is awesome. I added something to make parted idempotent, but it's hard to post it out of context, so...here is the full solution that i put together using the OP's script and @shubham-jairath 's excellent loop:

File: plays/mountdisk.yml

---
# https://stackoverflow.com/questions/63100027/how-to-mount-multiple-disks-in-a-loop-using-ansible

- name: get volume name
  shell: echo "/dev/$(ls -l /dev/disk/azure/scsi1 | grep {{ item.lun }} | egrep -o "([^\/]+$)")"
  register: volumename

- name: get partition (if any)
  shell: blkid | grep "{{ volumename.stdout }}" || true
  register: partition

- name: partition and mount
  block:
  - parted:
      device: "{{ volumename.stdout }}"
      number: 1
      state: present

  - filesystem:
      fstype: xfs
      dev: "{{ volumename.stdout }}"

  - mount:
      fstype: xfs
      opts: noatime
      src: "{{ volumename.stdout }}"
      path: "{{ item.mountpoint }}"
      state: mounted

  - command: blkid -s UUID -o value {{ volumename.stdout }}
    register: volumename_disk

  - blockinfile:
      path: /etc/fstab
      state: present
      block: |
        UUID={{ volumename_disk.stdout }}   {{ volumename.stdout }}      xfs defaults,noatime,nofail 0 0
  when: partition.stdout == ""

Upvotes: 0

Shubham Jairath
Shubham Jairath

Reputation: 392

After reading through documentation, I was able to find a way to achieve this.

The right approach would be to use Ansible's dictionary variable.

raid_config:
  - lun:          "{{ backup_lun }}"
    mountpoint:   "{{ backup_mountpoint }}"
  - lun:          "{{ data_lun }}"
    mountpoint:   "{{ data_mountpoint }}"
  - lun:          "{{ binlog_lun }}"
    mountpoint:   "{{ binlog_mountpoint }}"
  - lun:          "{{ innodb_lun }}"
    mountpoint:   "{{ innodb_mountpoint }}"

Moved the tasks to a separate file mountdisk.yml and passed a dictionary to the tasks like this

- include_tasks: "mountdisk.yml"
  loop: "{{ raid_config }}"
  loop_control:
    loop_var: disk
  tags: test
This way I was able to fetch lun and mount point as disk.lun and disk.mountpoint. Note: you would need to pass tags keyword separately in each task (if you are using one)while using include_tasks option.

Upvotes: 1

user1098490
user1098490

Reputation: 488

First of all I'm not an expert with ansible, but what it look like you're trying to do is:

  • Use the same loop for multiple tasks.

Which I think is not possible. The other thing is that the loop-variables is only available to the task with matching indentation, in your case: blockinfile.

What you can do is create an own task-file for the tasks you want perform and include that in your playbook (loop over includes). This has been done by @Konstantin in following example: Multiple ansible task with same loop

EXTRA:
Reason why it is NOT available
IF it had been possible to use a single loop for several tasks, then you would have to make sure to apply it to all the tasks, like:

- block:
    task1
     ...
    task2
     ...
  loop:
    - { lun: 'lun0', mountpoint: '/apps/mysql/binlog', diskname: 'binlog' }
    - { lun: 'lun1', mountpoint: '/apps/mysql/innodb', diskname: 'innodb' }

EXAMPLE:

(Created an example from your comment, I do not think this is a good solution, but it is a solution) Create a list using set_fact and then pass this list to another playbook.
Using my existing examples:

my_tasks.yml:

---
- command: echo "{{ item.lun}}"
  register: volumename
- set_fact:
    volumenames: "{{ volumenames|default([]) + [ { 'name': volumename.stdout } ] }}"

- debug:
    msg: "VOLUMENAME is: {{ volumename.stdout }}"

playbook.yml:

---
- hosts: localhost
  tasks:
    - include_tasks:   my_tasks.yml
      loop:
        - { lun: 'lun0', mountpoint: '/apps/mysql/binlog', diskname: 'binlog' }
        - { lun: 'lun1', mountpoint: '/apps/mysql/innodb', diskname: 'innodb' }
        - { lun: 'lun2', mountpoint: '/apps/mysql/data', diskname: 'data' }
        - { lun: 'lun3', mountpoint: '/apps/mysql', diskname: 'backup' }

- import_playbook: playbook2.yml myvar='{{ volumenames }}'

playbook2.yml

---
- hosts: localhost
  tasks:
    - debug:
        msg: "{{ myvar }}"


Upvotes: 2

Related Questions