Skip to content

Instantly share code, notes, and snippets.

@blakeblackshear
Last active September 24, 2015 15:08
Show Gist options
  • Save blakeblackshear/98bb6f45b42cc9117cec to your computer and use it in GitHub Desktop.
Save blakeblackshear/98bb6f45b42cc9117cec to your computer and use it in GitHub Desktop.

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment