Ansible get all hosts in subgroup - ansible

I have the following hosts structure in the inventory:
all:
children:
sc:
hosts:
sc-finder01a.com:
sc-finder01b.com:
vars:
default_port: 5679
version: 0.4.2-RELEASE
ms:
hosts:
ms-finder01a.com:
ms-finder01a.com:
vars:
default_port: 5679
version: 0.4.2-RELEASE
I'm running on all hosts, where for each host I'd like to access the other one in the subgroup (sc/ms) in order to check a condition before executing it on the current host, but I'm struggling to find the syntax. Also, I have to prevent Ansible from executing the command on two hosts in the same subgroup in parallel.
Ideas?

Have a look at ansible's special variables. For controlling execution, check playbook strategies.
You can access the current host's groups with the group_names variable. If you want to be in control of host execution order, delegate_to and run_once may help, where you assign a run_once controlling server and delegate tasks looping over the group members list.

Q: "Access the other hosts in the subgroup (sc/ms) in order to check a condition before executing it on the current host."
A: Iterate the list of the hosts in the subgroup(s), e.g.
- debug:
msg: "Check a condition on {{ item }}"
loop: "{{ group_names|map('extract', groups)|flatten|unique|
difference([inventory_hostname]) }}"
gives
TASK [debug] *******************************************************
ok: [sc-finder01b.com] => (item=sc-finder01a.com) =>
msg: Check a condition on sc-finder01a.com
ok: [sc-finder01a.com] => (item=sc-finder01b.com) =>
msg: Check a condition on sc-finder01b.com
ok: [ms-finder01b.com] => (item=ms-finder01a.com) =>
msg: Check a condition on ms-finder01a.com
ok: [ms-finder01a.com] => (item=ms-finder01b.com) =>
msg: Check a condition on ms-finder01b.com
Debug
The inventory
shell> cat hosts
all:
children:
sc:
hosts:
sc-finder01a.com:
sc-finder01b.com:
vars:
default_port: 5679
version: 0.4.2-RELEASE
ms:
hosts:
ms-finder01a.com:
ms-finder01b.com:
vars:
default_port: 5679
version: 0.4.2-RELEASE
Display all hosts
- debug:
var: groups
run_once: true
gives
TASK [debug] ***************************************************************
ok: [sc-finder01a.com] =>
groups:
all:
- sc-finder01a.com
- sc-finder01b.com
- ms-finder01a.com
- ms-finder01b.com
ms:
- ms-finder01a.com
- ms-finder01b.com
sc:
- sc-finder01a.com
- sc-finder01b.com
ungrouped: []
Multiple subgroups
The code works also if a host is a member of multiple subgroups, e.g.
shell> cat hosts
all:
children:
sc:
hosts:
sc-finder01a.com:
ms:
hosts:
ms-finder01a.com:
foo:
hosts:
foo-bar.com:
sc-finder01a.com:
ms-finder01a.com:
gives
TASK [debug] ******************************************************
ok: [sc-finder01a.com] => (item=foo-bar.com) =>
msg: Check a condition on foo-bar.com
ok: [sc-finder01a.com] => (item=ms-finder01a.com) =>
msg: Check a condition on ms-finder01a.com
ok: [foo-bar.com] => (item=sc-finder01a.com) =>
msg: Check a condition on sc-finder01a.com
ok: [ms-finder01a.com] => (item=foo-bar.com) =>
msg: Check a condition on foo-bar.com
ok: [foo-bar.com] => (item=ms-finder01a.com) =>
msg: Check a condition on ms-finder01a.com
ok: [ms-finder01a.com] => (item=sc-finder01a.com) =>
msg: Check a condition on sc-finder01a.com

Related

Rendering different template to each host in Ansible

Suppose I have a template file, containing, among others, a /24 IP range:
...
podCIDR: 192.168.<x>.0/24
...
Now, using Ansible, I want to render this template, with a running number from 1 to the number of hosts in my inventory, so that each host will have a different range. The first will have 192.168.1.0/24, the second 192.168.2.0/24, etc.
How do I do this?
Declare the index variable in the group_vars/all and use it to create the network
shell> cat group_vars/all
my_index1: "{{ ansible_play_hosts_all.index(inventory_hostname) + 1 }}"
podCIDR: "192.168.{{ my_index1 }}.0/24"
Then, given the inventory
shell> cat hosts
host_1
host_2
host_3
the playbook below
shell> cat pb.yml
- hosts: all
tasks:
- debug:
var: my_index1
- debug:
var: podCIDR
gives abridged
TASK [debug] *********************************************************************************
ok: [host_1] =>
my_index1: '1'
ok: [host_2] =>
my_index1: '2'
ok: [host_3] =>
my_index1: '3'
TASK [debug] *********************************************************************************
ok: [host_1] =>
podCIDR: 192.168.1.0/24
ok: [host_2] =>
podCIDR: 192.168.2.0/24
ok: [host_3] =>
podCIDR: 192.168.3.0/24
See:
Special Variables
Variable precedence: Where should I put a variable?
Finding the index of an item in a list

Running ansible playbook in serial over hostgroups

Im trying to run a playbook for n host groups serially (but 100% parallel within the host group). How do I achieve this?
I've tried things like:
- name: Test
hosts: group1:group2
serial: 100%
and even
- name: Test
hosts: group1:group2
serial: 1
Thinking it would do group by group, however these do not work.
How do I get it to run over all of group1, then after, all of group2 (but fail if anything in group1 fails)?
Also, how do I get it to run over n groups? (There are many hostgroups, which might be tough to define in the hosts key)
You can't control a playbook from another playbook. You'll have to control the playbook from outside, for example by a script. Given the inventory
shell> cat hosts-497
[group1]
srv1
[group2]
srv2
srv3
[group3]
srv4
srv5
srv6
and the playbook
shell> cat test-497.yml
- name: Test
hosts: all
gather_facts: false
tasks:
- debug:
msg: "{{ '%H:%M:%S'|strftime }}: {{ inventory_hostname }}"
the debug task is executed in parallel by all hosts
shell> ansible-playbook -i hosts-497 test-497.yml
PLAY [Test] ***************************************************************
TASK [debug] **************************************************************
ok: [srv3] =>
msg: '20:51:30: srv3'
ok: [srv1] =>
msg: '20:51:30: srv1'
ok: [srv4] =>
msg: '20:51:30: srv4'
ok: [srv2] =>
msg: '20:51:30: srv2'
ok: [srv5] =>
msg: '20:51:30: srv5'
ok: [srv6] =>
msg: '20:51:30: srv6'
If you want to control the hosts create a script and iterate the groups, e.g.
shell> cat test-497.sh
#!/usr/bin/sh
for i in group1 group2 group3; do
ansible-playbook -i hosts-497 --limit $i test-497.yml
done
gives (abridged)
shell> ./test-497.sh
PLAY [Test] *************************************************************
TASK [debug] ************************************************************
ok: [srv1] =>
msg: '20:56:41: srv1'
PLAY [Test] *************************************************************
TASK [debug] ************************************************************
ok: [srv3] =>
msg: '20:56:45: srv3'
ok: [srv2] =>
msg: '20:56:45: srv2'
PLAY [Test] *************************************************************
TASK [debug] ************************************************************
ok: [srv5] =>
msg: '20:56:52: srv5'
ok: [srv6] =>
msg: '20:56:52: srv6'
ok: [srv4] =>
msg: '20:56:53: srv4'

How to traverse an ansible list with a variable index?

There is a list as:
list:
- zero
- one
- two
For which:
list.0 -> zero
list.1 -> one
list.2 -> two
Is there a way where we could use a variable instead of using 0,1,2, etc?
Something like we do in any high-level-programming language:
(In a loop from var=0 to var=2){
print(list.var)
}
Use range. For example
- hosts: localhost
vars:
start: 0
stop: 3
list:
- zero
- one
- two
tasks:
- debug:
msg: "{{ list[item] }}"
loop: "{{ range(start, stop) }}"
gives
ok: [localhost] => (item=0) =>
msg: zero
ok: [localhost] => (item=1) =>
msg: one
ok: [localhost] => (item=2) =>
msg: two

Ansible: obtain a variable from a group of host

I have a group of server like:
[mygroup]
server1
server2
server3
Is there a way in ansible to have a variable that is the result of the concatenation of all hosts in the group mygroup?:
Example:
server1:80,server2:80,server3:80
You could do something like this:
- hosts: localhost
gather_facts: false
tasks:
- debug:
msg: "{{ groups.mygroup|map('regex_replace', '(.*)', '\\1:80')|list|join(',') }}"
If I have the hosts host0, host1, and host2 in my inventory, this produces:
TASK [debug] **********************************************************************************
ok: [localhost] => {
"msg": "host2:80,host1:80,host0:80"
}

How to loop through inventory and assign value in Ansible

I have a task in my Ansible playbook that I'm wanting iterate over each host in the group that I have and for each host I would like to assign a name from the hostname list that I've created in the vars folder.
I'm familiar with looping through inventory already by writing loop: "{{ groups['mygroup'] }}" and I have a list of hostnames I would like to assign each IP in 'mygroup' within the host file.
# In tasks file - roles/company/tasks/main.yml
- name: change hostname
win_hostname:
name: "{{ item }}"
loop: "{{ hostname }}"
register: res
# In the Inventory file
[company]
10.0.10.128
10.0.10.166
10.0.10.200
# In vars - roles/company/vars/main.yml
hostname:
- GL-WKS-18
- GL-WKS-19
- GL-WKS-20
# site.yml file located under /etc/ansible
- hosts: company
roles:
- common
- company #This is where the loop exists mentioned above.
# Command to run playbook
ansible-playbook -i hosts company.yml
I seem to have the individual pieces down or know about it, but how can I combine iterating over hosts from an inventory group and assign names that I have in an already created list (in roles vars folder) already?
UPDATE
the task mentioned above has been updated to reflect changes mentioned in answer:
- name: change hostname
win_hostname:
name: "{{ item.1 }}"
loop: {{ groups.company|zip(hostname)|list }}"
register: res
However the output I'm getting is incorrect, this should not run 9 times rather only three times, once per IP in the [company] group in the inventory. Also there are only three hostnames in a list that need to be assigned to each of the hosts in the inventory sheet.
changed: [10.0.10.128] => (item=[u'10.0.10.128', u'GL-WKS-18'])
changed: [10.0.10.166] => (item=[u'10.0.10.128', u'GL-WKS-18'])
changed: [10.0.10.200] => (item=[u'10.0.10.128', u'GL-WKS-18'])
changed: [10.0.10.128] => (item=[u'10.0.10.166', u'GL-WKS-19'])
changed: [10.0.10.166] => (item=[u'10.0.10.166', u'GL-WKS-19'])
changed: [10.0.10.200] => (item=[u'10.0.10.166', u'GL-WKS-19'])
ok: [10.0.10.128] => (item=[u'10.0.10.200', u'GL-WKS-20'])
ok: [10.0.10.166] => (item=[u'10.0.10.200', u'GL-WKS-20'])
ok: [10.0.10.200] => (item=[u'10.0.10.200', u'GL-WKS-20'])
Whenever I have a question about looping in Ansible I also go visit the Loops documentation. It sounds like you want to iterate over two lists in parallel, pairing an item from the list of hosts in your inventory with an item from the list of hostnames. In previous versions of Ansible that would suggest using the with_together loop, while with more recent versions of Ansible that suggests the zip filter (there's an example in the docs here).
To demonstrate this for your use case, I started with an inventory file that has three hosts:
[mygroup]
hostA ansible_host=localhost
hostB ansible_host=localhost
hostC ansible_host=localhost
And the following playbook:
---
- hosts: all
- hosts: localhost
gather_facts: false
vars:
hostnames:
- hostname01
- hostname02
- hostname03
tasks:
- name: change hostname
debug:
msg:
win_hostname:
name: "{{ item }}"
loop: "{{ groups.mygroup|zip(hostnames)|list }}"
Here I'm using a debug task instead of actually running the win_hostname task. The output of running:
ansible-playbook -i hosts playbook.yml
Looks like:
TASK [change hostname] ********************************************************************************************************************************
ok: [localhost] => (item=[u'hostA', u'hostname01']) => {
"msg": {
"win_hostname": {
"name": [
"hostA",
"hostname01"
]
}
}
}
ok: [localhost] => (item=[u'hostB', u'hostname02']) => {
"msg": {
"win_hostname": {
"name": [
"hostB",
"hostname02"
]
}
}
}
ok: [localhost] => (item=[u'hostC', u'hostname03']) => {
"msg": {
"win_hostname": {
"name": [
"hostC",
"hostname03"
]
}
}
}
As you can see, it's paired each host from the inventory with a hostname from the hostnames list.
Update
Based on the additional information you've provided, I think what you
actually want is this:
- name: change hostname
win_hostname:
name: "{{ hostnames[group.company.index(inventory_hostname) }}"
This will assign one value from hostname to each host in your
inventory. We're looking up the position of the current
inventory_hostname in your group, and then using that to index into
the hostnames list.

Resources