Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue looping on block module for a set of tasks in Ansible

I need to check if deploy.db file exists. If it does not exist, then I need to perform a set of tasks for which I'm using block.

Below is how i run the playbook

ansible-playbook test.yml -e Layer=APP -e BASEPATH="/logs" -e Filenames="file1,file2,file3"

Below is my complete playbook:

---
- name: "Play 1"
  hosts: localhost
  gather_facts: false
  tasks:
   - name: Construct 
     debug:
        msg: "Run"
   - block:
       - stat: path="{{ BASEPATH }}/deploy.db"
         register: currdb
       - file: path="{{ BASEPATH }}/deploy.db" state=touch recurse=no
         when: currdb.stat.exists == False
       - shell: "echo done>>{{ BASEPATH }}/deploy.db"
         when: currdb.stat.exists == False
     when: Layer == 'APP'
     with_items:
       - "{{ Filenames.split(',') }}" 

I'm getting the below error running the playbook:

ERROR! 'with_items' is not a valid attribute for a Block

The error appears to be in '/app/test.yml': line 9, column 6, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

   - block:
     ^ here

After researching a bit, I understand that neither with_items nor loop is supported by block module and the solution is to include task file.

I'm however, not sure how to get that to work. Can you please suggest what tweaks I need to inorder for my playbook to work ?

Considering i m on the latest version of Ansible are there other solutions.

like image 648
Ashar Avatar asked Nov 18 '19 05:11

Ashar


People also ask

Can we use loop in block Ansible?

Ansible allows you to loop over a list of hashes. This can be seen from the example given below.

Can we have multiple tasks in a Ansible playbook?

A Playbook can have multiple Plays and a Play can have one or multiple Tasks. In a Task a Module is called, like the Modules in the previous chapter. The goal of a Play is to map a group of hosts to a list of Tasks.

How do you handle long running tasks in Ansible?

For long running asynchronous tasks, it's good to set poll=0 so that Ansible can immediately jump to the next task after starting the current one without waiting for the result. Register: It is basically used to register the results on a task in a variable.

Can we loop over two parallel sets in Ansible?

Save this answer. Show activity on this post. To answer your question: No, as of now Ansible can't run loops in parallel.


1 Answers

TL;DR

'with_items' is not a valid attribute for a Block

The error message says it all: you cannot loop over a block.

If you need to loop over a set of tasks, put them in a separate file and use include_tasks

Implementation (and some good practice...)

Below is an implementation based on your example illustrating the solution.

Since your question and code lacks some precision and since I pointed out some bad practices, please note that:

  • I fixed the looped code to effectively use the filenames you loop on (I inferred it was supposed to the deploy.db file). Note the use of loop_control to disambiguate the variable name in the included file (i.e. db_filename).
  • I made the code idempotent as much as possible by using the ansible module copy in place of shell and dropped the touch phase.
  • I transformed the var names to all lowercase and underscore separator.
  • To make sure the copy task works on all occasion, I replaced the removed tasks with a single making sure the basepath dir exists.
  • I added a unique filter after filenames.split(',') as well as a trim filter on each value to remove possible duplicates and eventual spaces added by error in the coma separated list.
  • I used not keyword and bool filter (for extra security) rather than a bare compare to a boolean False value.

Here is the included file create_db_each.yml

---
- name: Check if file exists
  stat:
    path: "{{ basepath }}/{{ db_filename }}"
  register: currdb

- name: Create the file with "done" line if not present
  copy:
    content: "done"
    dest: "{{ basepath }}/{{ db_filename }}"
  when: not currdb.stat.exists | bool

used in the following create_db.yml playbook

---
- name: "Create my dbs"
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Make sure the base directory exists
      file:
        path: "{{ basepath }}"
        state: directory

    - name: load each db
      include_tasks: "create_db_each.yml"
      when: layer == 'APP'
      loop: "{{ filenames.split(',') | unique | map('trim') }}"
      loop_control:
        loop_var: db_filename

which gives


notes:

  • first run only, run it again on your side to witness it reports OK everywhere
  • see the filenames parameter value to illustrate the use of unique and trim

$ ansible-playbook -e basepath=/tmp/my/base/path -e "filenames='a.bla, b.toto, c , z.txt,a.bla'"  -e layer=APP create_db.yml

PLAY [Create my dbs] ************************************************

TASK [Make sure the base directory exists] **************************
changed: [localhost]

TASK [load each db] *************************************************
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=a.bla)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=b.toto)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=c)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=z.txt)

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

PLAY RECAP **********************************************************
localhost: ok=13   changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ tree /tmp/my/base/path/
/tmp/my/base/path/
├── a.bla
├── b.toto
├── c
└── z.txt

$ for f in /tmp/my/base/path/*; do cat $f; echo; done
done
done
done
done
like image 175
Zeitounator Avatar answered Nov 15 '22 04:11

Zeitounator