Running a playbook on multiple host groups one at a time

Shay Rybak picture Shay Rybak · Jun 20, 2017 · Viewed 39k times · Source

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:

Answer

Konstantin Suvorov picture Konstantin Suvorov · Jun 21, 2017

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