Ansible: filtrar una lista por sus atributos

83

Tengo una variable llamada "red" registrada en Ansible:

{
    "addresses": {
        "private_ext": [
            {
                "type": "fixed",
                "addr": "172.16.2.100"
            }
        ],
        "private_man": [
            {
                "type": "fixed",
                "addr": "172.16.1.100"
            },
            {
                "type": "floating",
                "addr": "10.90.80.10"
            }
        ]
    }
}

¿Es posible obtener la dirección IP ("addr") con type = "floating" haciendo algo como esto?

- debug: var={{ network.addresses.private_man | filter type="fixed" | get "addr" }}

Sé que la sintaxis es incorrecta, pero entiendes la idea.

Guido
fuente

Respuestas:

41

Envié una solicitud de extracción (disponible en Ansible 2.2+) que facilitará este tipo de situaciones al agregar soporte de consulta jmespath en Ansible. En su caso, funcionaría así:

- debug: msg="{{ addresses | json_query(\"private_man[?type=='fixed'].addr\") }}"

volvería:

ok: [localhost] => {
    "msg": [
        "172.16.1.100"
    ]
}
Filipe
fuente
3
Necesita instalar "jmespath" antes de ejecutar el filtro json_query.
hasta el
Y simplemente dejé de intentar instalar jmespath debido a las diferencias de versión de Python. No es una solución tan trivial como parece en Ansible 2.4+
Stuart Watt
1
@StuartWatt, es posible que necesite que pyenv se abstraiga de la versión de Python del sistema operativo. Puede probar pyenv, pyenv virtualenvs o algunas personas prefieren pipenv. Funcionarán de todos modos.
Jepper
@Jepper Si necesita un Python completamente nuevo para usar Ansible, es una batalla perdida. (Y estaba usando pyenv y todavía tenía estos problemas :-)
Stuart Watt
127

Para filtrar una lista de dictados, puede usar el filtro selectattr junto con la prueba equalto :

network.addresses.private_man | selectattr("type", "equalto", "fixed")

Lo anterior requiere Jinja2 v2.8 o posterior (independientemente de la versión de Ansible).


Ansible también tiene las pruebas matchysearch , que toman expresiones regulares:

matchrequerirá una coincidencia completa en la cadena, mientras searchque requerirá una coincidencia dentro de la cadena.

network.addresses.private_man | selectattr("type", "match", "^fixed$")

Para reducir la lista de dictados a una lista de cadenas, de modo que solo obtenga una lista de los addrcampos, puede usar el filtro de mapa :

... | map(attribute='addr') | list

O si quieres una cadena separada por comas:

... | map(attribute='addr') | join(',')

Combinado, se vería así.

- debug: msg={{ network.addresses.private_man | selectattr("type", "equalto", "fixed") | map(attribute='addr') | join(',') }}
udondan
fuente
1
Si los quiere como una lista en lugar de una cadena separada por comas, también puede usar:{{ network.addresses.private_man | selectattr("type", "equalto", "fixed") | map(attribute='addr') | list }}
TrinitronX
1
¿cambió el enlace que proporcionaste? No puedo encontrar "coincidencia" y "búsqueda" en esa página
activedecay
1
@activedecay Parece que se ha ido de esa página, sí. La única mención que pude encontrar ahora está en esta página: docs.ansible.com/ansible/latest/user_guide/… Actualicé el enlace, ¡gracias!
udondan
19

No necesariamente mejor, pero dado que es bueno tener opciones, aquí se explica cómo hacerlo usando declaraciones de Jinja :

- debug:
    msg: "{% for address in network.addresses.private_man %}\
        {% if address.type == 'fixed' %}\
          {{ address.addr }}\
        {% endif %}\
      {% endfor %}"

O si prefiere ponerlo todo en una sola línea:

- debug:
    msg: "{% for address in network.addresses.private_man if address.type == 'fixed' %}{{ address.addr }}{% endfor %}"

Que devuelve:

ok: [localhost] => {
    "msg": "172.16.1.100"
}
bmaupin
fuente