Running a playbook on multiple host groups one at a time
Asked Answered
E

3

15

I want to run a playbook containing some roles on multiple host groups I create dynamically with the group_by module.

I'm able to do it like the example below (ping replacing my actual role).

I was wondering if there is a way to run each group separately in a loop instead of listing all instance ids. I don't want to create a duplicate line with every instance id.

The purpose here is to deploy to one instance in every data center at a time instead of running all with a low serial which takes a long time.

There might be a different way of doing it, I don't want to create static groups in the inventory for each instance_id as well.

---
- hosts: tag_type_edgenode
  tasks:
    - group_by: key=instance_id_{{instance_id}}
      register: dyn_groups

- hosts: instance_id_1
  tasks:
    - ping:
- hosts: instance_id_2
  tasks:
    - ping:
- hosts: instance_id_3
  tasks:
    - ping:
- hosts: instance_id_4
  tasks:
    - ping:
Earthworm answered 20/6, 2017 at 11:30 Comment(0)
C
13

If you have equal count of hosts in each group, you can use pattern + serial.
Ansible forms host list by pattern moving through groups sequentially. So if you have equal count of hosts, then batches formed by serial will be equal to groups.

In your example, if you have exactly 3 hosts in each group, you can use:

- hosts: instance_id_*
  serial: 3
  tasks:
    - ping:

If you don't mind a bit of Ansible patching, you can modify _get_serialized_batches method.
Add this code just before while len(all_hosts) > 0::

    if 'serialize_by_var' in play.get_vars():
        param = play.get_vars()['serialize_by_var']
        sb = []
        def by_param(x):
            vrs = x.get_vars()
            if param in vrs:
                return vrs[param]
            else:
                return None

        s_hosts = sorted(all_hosts,key=by_param)
        for k, g in itertools.groupby(s_hosts, by_param):
            sb.append(list(g))

        display.vv('Serializing by host var "{}": {}'.format(param,sb))
        return sb

And you can serialize hosts by any variable like this:

- hosts: tag_type_edgenode
  vars:
    serialize_by_var: instance_id
  tasks:
    - ping
Colony answered 21/6, 2017 at 7:53 Comment(1)
The first option works just fine. it orders the hosts by groups for running.Earthworm
G
3

There is a simpler method using the size (length) of each group. However, this runs on all members of a group, flowing through the groups sequentially. I think OP was asking for how to act on the first member of each group, which I am also working on figuring out.

- name: "Restore selected devices in model_dc"
  hosts: group_1, group_2, group_3, group_4, group_5, group_6, group_7, !excluded
  any_errors_fatal: true    # Stop entire play if one host fails
  gather_facts: no
  order: inventory
  serial: 
    - "{{ groups['group_1'] | length }}" # The number of hosts for first batch
    - "{{ groups['group_2'] | length }}" # The number of hosts for second batch
    - "{{ groups['group_3'] | length }}"
    - "{{ groups['group_4'] | length }}"
    - "{{ groups['group_5'] | length }}"
    - "{{ groups['group_6'] | length }}"
    - "{{ groups['group_7'] | length }}" # The number of hosts for every batch until the end.
Giggle answered 10/12, 2020 at 19:59 Comment(0)
D
1

Building off of Konstantin's idea, you can do something like this using aliases and a list of patterns:

---
- hosts: "*-server-batch-1,*-servers-batch-2,*-server-batch-3"
  serial: 3
  ...
...


[london]
london-server-batch-1 ansible_host=server1.london.com
london-server-batch-2 ansible_host=server2.london.com
london-server-batch-3 ansible_host=server3.london.com

[tokyo]
tokyo-server-batch-1 ansible_host=server1.tokyo.com
tokyo-server-batch-2 ansible_host=server2.tokyo.com
tokyo-server-batch-3 ansible_host=server3.tokyo.com
Defrost answered 12/9, 2017 at 22:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.