Ansible: how to call module `add_host` for all hosts of the play
Asked Answered
M

3

10

I'm creating a playbook with this play:

On hosts hypervisors:

  • retrieve list of virtual machines from all hosts
  • use module add_host to add all of them in a new inventory group called guests

My inventory:

[hypervisors]
host1
host2

My playbook:

- hosts: hypervisors
  - shell: virsh list | awk 'NR>2' | awk '{print $2}'
    register: result_virsh
  - add_host:
      name: "{{ item }}"
      group: "guests"
    with_items: "{{ result_virsh.stdout_lines }}"

Module add_host bypasses the play host loop and only runs once for all the hosts in the play.

Then it is called once (for host1), it's a particular case of the use of this module (see link above), as if the variable run_once was implicitly fixed to true.

How can I use it for all hosts in group hypervisors ?

EDIT: Example to reproduce it on your computer with only localhost

Create file /tmp/host1_test to simulate a return of guests vm1 and vm2:

vm1
vm2

Create file /tmp/host2_test to simulate a return of guests vm3 and vm4:

vm3
vm4

Use this inventory (test_add_host.ini) with two hosts, both with fixed IP address 127.0.0.1:

[hypervisors]
host1 ansible_host=127.0.0.1 test_filename=/tmp/host1_test
host2 ansible_host=127.0.0.1 test_filename=/tmp/host2_test

Use this playbook (test_add_host.yml):

- hosts: hypervisors
  gather_facts: no
  tasks:
  - shell: "cat {{ test_filename }}"
    register: result_virsh
  - add_host:
      name: "{{ item }}"
      group: "guests"
    with_items: "{{ result_virsh.stdout_lines }}"

- hosts: guests
  gather_facts: no
  tasks:
  - local_action: ping

Call this playbook locally with command:

ansible-playbook -c local -i test_add_host.ini test_add_host.yml
  • First play call hosts host1 and host2
  • Second play call hosts vm1 and vm2

What should I do to call all hosts (vm1, vm2, vm3 and vm4) in second play ?

Mattress answered 8/2, 2017 at 6:52 Comment(0)
D
15

As you noted, there's a thing about add_host: BYPASS_HOST_LOOP = True.
So it's a kind of forced run_once.

If you don't mind running over hypervisors in sequential manner, you can simply use serial: 1:

- hosts: hypervisors
  serial: 1
  tasks:
    - shell: virsh list | awk 'NR>2' | awk '{print $2}'
      register: result_virsh
    - add_host:
        name: "{{ item }}"
        group: "guests"
      with_items: "{{ result_virsh.stdout_lines }}"

This ensures that every play batch consists of only one host, so add_host executes for every host.

Double answered 8/2, 2017 at 10:16 Comment(1)
Argh! I don't mind running hypervisors in serial, It was so simple in fact! THANKS!Mattress
C
4

If you don't want to run the play serially you can aggregate the results with ansible_play_hosts and map. The results can be used in the next play.

- hosts: all
  gather_facts: false
  tasks:
    - shell: virsh list | awk 'NR>2' | awk '{print $2}'
      register: result_virsh
      changed_when: false

    - add_host:
        name: "{{ item }}"
        group: guests
      changed_when: false
      loop: "{{ ansible_play_hosts | map('extract', hostvars, 'result_virsh') | map(attribute='stdout_lines') | flatten }}"

- hosts: guests
  gather_facts: false
  tasks:
    - ping:

This answer was derived from Ansible: Accumulate output across multiple hosts on task run.

Cavicorn answered 25/7, 2019 at 20:42 Comment(0)
M
0

I solved this problem (with my localhost example) with following playbook. This solution is very complex, if you have a simpler one, shared it!

I didn't want to use dynamic inventories

# Get list of virtual machines in hostvars[inventory_hostname].vms
- hosts: hypervisors
  gather_facts: no
  tasks:
    - shell: "cat {{ test_filename }}"
      register: result_virsh
    - set_fact:
        vms: "{{ result_virsh.stdout_lines }}"

# Remove previous vm_hosts file
- hosts: localhost
  gather_facts: no
  tasks:
    - file:
        path: /tmp/vm_hosts
        state: absent

# Build file vm_hosts with list of virtual machines in serial (working in parallele with same file cause some troubles)
- hosts: hypervisors
  gather_facts: no
  serial: 1
  tasks:
    - block:
        - file:
            path: /tmp/vm_hosts
            mode: 0644
            state: touch
          run_once: yes
        - lineinfile:
            dest: /tmp/vm_hosts
            line: '{{ item }}'
          with_items: "{{ hostvars[inventory_hostname].vms }}"
      delegate_to: localhost

# Add list of virtual machines from file vm_hosts to in-memory inventory
- hosts: localhost
  gather_facts: no
  tasks:
  - add_host:
      name: "{{ item }}"
      group: "guests"
    with_lines: cat /tmp/vm_hosts

- hosts: guests
  gather_facts: no
  tasks:
    - local_action: ping
Mattress answered 8/2, 2017 at 10:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.