Search code examples
ansiblersync

Ansible - Run Task Against Hosts in a with_together Fashion


I am currently taking two hosts and dynamically adding them to a group, followed by a synchronize task using with_together to use 3 lists of 2 elements in parallel to copy the specified files between two remote servers.

Here's an example based on the idea:

---
- name: Configure Hosts for Copying
  hosts: localhost
  gather_facts: no
  tasks:

    - name: Adding given hosts to new group...
      add_host:
        name: "{{ item }}"
        groups: copy_group
      with_items:
        - ["remoteDest1", "remoteDest2"]


- name: Copy Files between servers
  hosts: copy_group
  gather_facts: no
  tasks:    

    - name: Copying files...
      synchronize:
        src: "{{ item[1] }}"
        dest: "{{ item[2] }}"
      with_together:
        - ["remoteSrc1", "remoteSrc2"]
        - ["/tmp/remote/source/one/", "/tmp/remote/source/two/"]
        - ["/tmp/remote/dest/one/", "/tmp/remote/dest/two/"]
      delegate_to: "{{ item[0] }}"

Currently, it does both operations for both servers, resulting in 4 operations.

I need it to synchronize like so:

  • copy /tmp/remote/source/one/ from remoteSrc1 to /tmp/remote/dest/one/ on remoteDest1
  • copy /tmp/remote/source/two/ from remoteSrc2 to /tmp/remote/dest/two/ on remoteDest2

Which would mean it's a 1:1 ratio; essentially acting on the hosts in the same manner as with_together does for the lists.

The hosts are obtained dynamically, so I can't just make a different play for each host.

Since synchronize is essentially simplified version of rsync, then if there's a simple solution for this using rsync directly, then it would be much appreciated.


Solution

  • There isn't native functionality for this, so this is how I solved it:

    Given the original task, add the following two lines:

      - "{{ groups['copy_group'] }}"
    when: inventory_hostname == item[3]
    

    To get:

    - name: Copying files...
      synchronize:
        src: "{{ item[1] }}"
        dest: "{{ item[2] }}"
      with_together:
        - ["remoteSrc1", "remoteSrc2"]
        - ["/tmp/remote/source/one/", "/tmp/remote/source/two/"]
        - ["/tmp/remote/dest/one/", "/tmp/remote/dest/two/"]
        - "{{ groups['copy_group'] }}"
      delegate_to: "{{ item[0] }}"
      when: inventory_hostname == item[3]
    

    Essentially, by adding the hosts as a list, they can be used in the when statement to execute the task only when the current host (inventory_hostname) matches the host currently being indexed in the list.

    The result is that the play only runs against each host once in a serial manner with the other list items that have the same index.