Sometimes you need to grab a particular variable from all hosts in a group in order to pass it into another role. For example, lets say you have a dns_servers
group in your inventory, and you want to get the ip addresses for all servers in that group so you can configure the remaining servers to use them for DNS queries.
Ansible gives you variables about hosts in the hostvars
object structured as shown below (i have omitted most of the vars for clarity):
{
"dns-1": {
"ansible_local": {
"net": {
"internal": {
"ip": "10.0.0.11"
}
}
}
},
"dns-2": {
"ansible_local": {
"net": {
"internal": {
"ip": "10.0.0.12"
}
}
}
}
}
At first you might think you can select this from hostvars
, filter by the groups, and grab the ip address attribute. You can lookup the variables for a particular host with hostvars[<hostname>]
. However, this set of variables does not include which groups the host is in, so you need to include the information in the groups
variable.
Ansible gives you information about the groups through the groups
variable in the structure shown below:
{
"all": [
"dns-1",
"dns-2"
],
"dns_servers": [
"dns-1",
"dns-2"
]
}
You can get a list of hosts in a particular group with groups[<group_name]
. However this doesn't include any of the variables for each host, so you can't loop over that either.
Unfortunately, with the built in set of filters, you cannot loop over all hosts in a group and grab a single property and put it in an array. In order to do this, we need the variables structured like so:
[
{
"ansible_local": {
"net": {
"internal": {
"ip": "10.0.0.11"
}
}
}
},
{
"ansible_local": {
"net": {
"internal": {
"ip": "10.0.0.12"
}
}
}
}
]
This allows you to pass the list to other filters to get the ip address property that you want.
In order to do this, you need a custom filter to output an array of all the hostvars for all hosts in a particular group. This can be done like so:
from ansible import errors, runner
import json
def to_group_vars(host_vars, groups, target = 'all'):
# if the group name doesnt exist, just return an empty list
if target not in groups:
return [];
# create a list to put the hostvars in
data = []
# loop over the hosts in the group
for host in groups[target]:
# add the hostvars for that group to the list
data.append(host_vars[host])
# return the list of hostvars
return data
class FilterModule (object):
def filters(self):
return {"to_group_vars": to_group_vars}
Now you can use builtin filters to get you the rest of the way there.
hostvars|to_group_vars(groups, 'dns_servers')|map(attribute='ansible_local.net.internal.ip')|list
This can be broken down like so:
Filter | Description |
---|---|
to_group_vars(groups, 'dns_servers') |
get an array of hostvars for all servers in the dns_servers group |
map(attribute='ansible_local.net.internal.ip') |
pluck out the internal ip attribute |
list |
convert it to a list |