ansiblejinja2

How to filter a list where an attribute is variable?


The title is a bit confusing but I didn't know how else to describe it. My hostvars look like this:

{
  "host1": {
    "tags": {
      "Name": "host1-name"
    }
  },
  "host2": {
    "tags": {
      "Name": "host2-name"
    }
  }
}

I don't know which host I want to capture--I need to get that dynamically based on the results of the Name tag. I tried this:

hostvars | selectattr("tags.Name", "equalto", "host2-name")

But of course the tags attribute isn't at the root so it errors out. The problem is the root attribute is variable (host1, host2, etc.).

I tried to use an asterisk, but I got "VARIABLE IS NOT DEFINED!: 'str object' has no attribute '*'":

hostvars | selectattr("*.tags.Name", "equalto", "host2-name")

So how would I filter based on the Name tag, where I don't know what the root attribute is?


Solution

  • The selectattr filter is meant for list of dicts. You are trying to apply it to hostvars which is not a list but a dictionary.

    To perform your search you'll first need to transform that dict to a list. There are basically two options to do this:

    1. Pass the var to the dict2items filter to get a list of key/value pairs (i.e. key=host_name_string, value=dict_of_host_variables)
    2. Completely drop the keys and keep only the values (i.e. the dictionary of the actual vars for the host).

    The solution to choose depends on your requirement. I this example I'll take the second one as we will not loose any information. In fact the key we will drop will still exist inside the value as the inventory_hostname variable.

    Here is how to filter your hosts with this solution:

    matching_hosts_list: "{{ hostvars.values() | selectattr('tags.Name', "==", "AIR-Media-Release-SW5") }}"
    

    From there you can use the result as you wish. For example, to print a list of matching host names:

    - name: Show matching host names
      ansible.builtin.debug:
        var: matching_hosts_list | map(attribute='inventory_hostname')