Assume that a group has 10 hosts.
How to run playbook on N hosts from 10. N is arbitrary value from 1 to 10.
Example:
- hosts: groups['group_name'][1:3] it works.
But it doesn't work if instead of 3 I use variable, like this
- hosts: groups['group_name'][1:N]
It could be random N, first N, last N what ever.
Thank you.
This work absolutely fine in ansible 2.1:
---
- hosts: all
gather_facts: no
tasks:
- group_by: key=limited_selection
when: play_hosts.index(inventory_hostname) < max_index | int
- hosts: limited_selection
gather_facts: no
tasks:
- debug: msg="I'm in the limited selection group!"
Exec like this: ansible-playbook -e max_index=3 playbook.yml or define max_index somewhere else.
Related
input:
- tests_to_run
tasks:
modify_user_input:
< Remove the empty elements from tests_to_run>
Have been searching for hours. Does anyone know a way to filter the input, so I can manipulate the tests_to_run list, so it does not have any empty elements.
Sample input and output
Sample tests_to_run = ['test_a','','test_b','test_c','yrls']
Expected variable to be used in later tasks test_to_run_clean = ['test_a','test_b','test_c','yrls']
To remove the empty element from the list:
---
- name: Sample playbook
connection: local
gather_facts: false
hosts: localhost
vars:
mylist:
- abc
- def
-
- jkl
tasks:
- set_fact:
mylist: "{{ mylist|select()|list }}"
I have a dynamic inventory which returns me my hosts addresses.
But sometimes, I would like to apply some configuration to a limited number of hosts.
A sample with N hosts but only 5 are echoed:
- name: "Run silly shell script"
shell: |
echo {{ item }}
exit 0
with_items: "{{ hosts | only(5) }}"
to get the first X elements from a list, use: list_var[:X].
full example as PB below:
---
- hosts: localhost
gather_facts: false
vars:
list_var:
- 1
- 2
- 3
- 4
- 5
- 6
tasks:
- name: print full list
debug:
var: list_var
- name: print list of first 3 elements
debug:
var: list_var[:3]
hope it helps.
Given the following playbook:
---
- name: test local_action with_items
hosts: localhost
gather_facts: false
tasks:
- name: "add something to a file"
shell: echo '{{item}}' >> foo.txt
with_items:
- "aaaaa"
- "aaaaa"
# using 40 items
or
---
- name: test local_action with_items
hosts: localhost
gather_facts: false
tasks:
- name: "add something to a file"
shell: echo '{{item}}' >> foo.txt
with_sequence: count=40
The latter playbook run 5 seconds.
Using a bash loop is obviously much (1000 times) faster and takes 5 ms:
time for i in $(seq 1 40); do echo $i >> foo.txt; done
Now it is clear that Ansible has some overhead, but is there any possibility to speed this up?
Instead of shell module, use raw module. It will be as quick as the bash loop.
---
- name: test local_action with_items
hosts: localhost
gather_facts: false
tasks:
- name: "add something to a file"
raw: echo '{{item}}' >> foo.txt
with_sequence: count=40
...
Anyway, if you want performance, maybe write your code in C.
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:
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
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.
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
I have a playbook with multiple plays:
---
- hosts: druid-realtime-1
sudo: true
roles:
- { role: druid-realtime, du_rt_id: 1 }
- hosts: druid-realtime-2
sudo: true
roles:
- { role: druid-realtime, du_rt_id: 2 }
How do I tell ansible to run both plays in parallel instead of one after another?
You could do it this way
In your Ansible inventory, group your servers and assign a host variable:
[druid-realtime]
druid-realtime-1 id=1
druid-realtime-2 id=2
Then reference the variable in the playbook:
- hosts: druid-realtime
sudo: true
roles:
- { role: druid-realtime, du_rt_id: {{ id }} }
Not sure if this was possible when this post was created but I believe this is what you were looking for:
http://docs.ansible.com/ansible/playbooks_async.html
This allows you to stop the task from blocking, ie. waiting for the task to complete before progressing onto the next task.
To run multiple playbooks in parallel I wrote ansible-parallel today.
So for your question, split the plays in different yml files and run:
pip install ansible-parallel
ansible-parallel *.yml
If your plays are isolated, you can split your playbook : 1 play -> 1 playbook. For example:
druid-realtime-1.yml:
- hosts: druid-realtime-1
sudo: true
roles:
- { role: druid-realtime, du_rt_id: 1 }
druid-realtime-2.yml:
- hosts: druid-realtime-2
sudo: true
roles:
- { role: druid-realtime, du_rt_id: 2 }
Keep a main playbook site.yml that includes other playbooks:
- include: druid-realtime-1.yml
- include: druid-realtime-2.yml
With this approach, you can use a terminal for each playbooks and continue to use your main playbook.