How to execute a loop of actions on differents hosts?

I would like to loop on a set of tasks which are executed on different hosts.
For example:

  • action 1 on host A
  • action 2 on host B
  • action 3 on host C
    repeated for each element of my variable TestName

Please, tell us more about TestName. Is it:

  • a list of lists,
  • a list of dicts,
  • a list of actions you want to execute on a fixed set of hosts A, B, and C,
  • a list of hosts you want to execute a fixed set of actions 1, 2, and 3, on…

It can be difficult to see how many ways other people might interpret a statement made when you’re thinking of a particular scenario. In this case there are too many nuances left out, and community.general.read_my_mind is still not even on the roadmap.

A more complete description (or better, actual example(s)) of TestName would get you better responses.

3 Likes

Hi @utoddl,

TestName is a list of string in my case.

Best Regards

… and those strings are names of actions? or hosts?

These strings are labels which are used by action1/action2/action3 as functional parameters.

Can you please show examples?
Can you show us what you’ve tried that hasn’t worked?

It’s really hard to give specific answers to vaguely general problem descriptions, and bringing in even more general terms doesn’t add clarity. Ansible supports action plugins: somehow I don’t think that’s what you mean by “actions”. Ansible doesn’t support functional programming (except in a few accidental cases via Jinja2), and I don’t think that’s what you mean by “functional parameters” either.

Sorry to sound impatient in a public forum. We really do want to help you solve your problem, but you’ve got to help us help you.

3 Likes

Hi @utoddl ,
My variable is defined in group_vars/all as below:

test_format: 
  - svsGlissando_10000
  - svsGlissando_20000
  - svsGlissando_30000
  - svsGlissando_40000

I currently have a central playbook wich imports another playbook multiple times with different values of test_name.

- import_playbook: ../common/launch_test_retrieve_logs.yml
  vars:
    var_file: "{{ var_file }}"
    test_name: "svsGlissando_10000"
  when: test_name in test_format

- import_playbook: ../common/launch_test_retrieve_logs.yml
  vars:
    var_file: "{{ var_file }}"
    test_name: "svsGlissando_20000"
  when: test_name in test_format

- import_playbook: ../common/launch_test_retrieve_logs.yml
  vars:
    var_file: "{{ var_file }}"
    test_name: "svsGlissando_30000"
  when: test_name in test_format

I have this same block hundrerds of time in my main playbook which I would like to avoid :frowning:
Playbook launch_test_retrieve_logs.yml also uses import_playbook but on different playbooks which are executed on different hosts

- import_playbook: launch_test.yml

- import_playbook: retrieve_apache_logs.yml
  when: webserver == 'apache'

- import_playbook: restart_jmeter_worker.yml

launch_test.yml has for hosts jmeter-controller and launch jmeter based on configuration files which use test_name
retrieve_apache_logs.yml has for hosts web-apache retrieves logs files and store them in a path which use test_name
restart_jmeter_worker.yml has for hosts jmeter-workers

This behavior is working fine but is not very elegant as for each new value of test_format I add in the variable I also must create a new block in my main playbook.

Now you’re talkin’! This is beautiful.

Well, as a request for help, it’s beautiful compared to your first attempt. :slight_smile: I wouldn’t have guessed this structure in a million years based on the initial description.

As an example of “How To Ansible”, it’s not so beautiful. The big problem — and understand, I’ve only been looking at this for a little while so I could be missing some subtleties — everything seems to be “up a level” above where it ought to be. I’m guessing that your playbooks should probably be roles, and your roles (if you have any) should probably be task files within those roles. Because what you want to do is something like this:

# N.B. This won't work!
- import_playbook: ../common/launch_test_retrieve_logs.yml
  vars:
    var_file: "{{ var_file }}"
  loop: '{{ test_format }}'
  loop_control:
    loop_var: test_name

But that won’t work, because you can’t loop over “PlaybookInclude” – an internal name apparently for what import_playbook is/does. At least, that’s in the error message. I tried it anyway.

Rather, what might accomplish your goal is more like:

---
# tests-playbook.yml
- name: Tests playbook
  hosts: localhost
  gather_facts: false
  vars:
    test_format:
      - svsGlissando_10000
      - svsGlissando_20000
      - svsGlissando_30000
      - svsGlissando_40000
      - svsTremolo_10000
      - svsTremolo_20000
  tasks:
    - name: Include the glissando test roles
      ansible.builtin.include_role:
        name: glissando_tests
        tasks_from: main.yml
      loop: '{{ test_format }}'
      when: test_name is search("Glissando")
      loop_control:
        loop_var: test_name

    - name: Include the tremolo test roles
      ansible.builtin.include_role:
        name: tremolo_tests
        tasks_from: main.yml
      loop: '{{ test_format }}'
      when: test_name is search("Tremolo")
      loop_control:
        loop_var: test_name

You’ve got more going on than this example solves, but I’m pretty sure your playbooks should be roles. Restructuring everything is not going to be trivial, but I expect it will be worth it. You’re basically shifting from a code-driven flow to a data-driven flow. That’s not trivial, but it is possible with roles; with playbooks – not so much.

Feel free to follow-up with additional questions, which I’m sure will pop up as you dig into the details.

2 Likes

Picking up on the “Glissando” part and totally overlooking the whole medical imaging, .svs thing, I naively extended your test_format list in a musical direction. It was only an example anyway, but I feel kind of silly. :blush:

1 Like