Ansible filters
No es ningún secreto que en nuestro día a día trabajamos con Ansible e intentamos automatizar el 100% de todos los sistemas. Dentro de nuestra metodología, en muchas ocasiones, utilizamos listas para invocar reiteradamente una tarea, un role, etc.
Esto nos permite normalizar bastante las automatizaciones e iterar en listas en casos como:
- Configuración de múltiples virtualhosts, donde la tarea/rol requiere ser instanciado una vez por cada virtualhost.
- Configuración de definiciones en kubernetes como deployments, ingress, etc., invocando 'N' veces para desplegar la definición de ficheros YAML.
- Configuración de distintas versiones de php, donde se invoca una tarea para instalar cada versión de php y configurar los php.ini pertinentes.
Es una gran ventaja a muchos niveles:
- Legibilidad.
- Mantenimiento de código.
- Reutilización de código.
Ahora bien, en otras ocasiones, si estas listas tienen muchos elementos, las ejecuciones de tareas/roles pueden ser largas (dependiendo de lo que hagan) prolongando mucho la ejecución de Ansible, o bien, puede darse el caso de no querer desplegar todas las configuraciones por algún motivo.
Para ello, hacemos uso del método "search" de Ansible y los condicionales "when".
Método "search" de Ansible y los condicionales "when"
El método search(), devuelve un boolean si encuentra la expresión regular el string facilitado. Y when... ya sabemos para qué lo utilizamos.
La forma de usar "search()" es => var_1 is search("exp"). Será true si el valor de la variable var_1 hace match con la expresión regular "exp".
Si sacamos el valor "exp" a una variable filter_example, quedaría de la siguiente forma:
---
filter_example: "exp"
---
var_1 is search(filter_example)
Como hemos dicho, search() busca expresiones regulares, por lo que usando ".*" como valor de filter_example, lo ejecutaríamos siempre.
Finalmente, si esto lo metemos en un condicional "when", tendríamos una ejecución de una tarea, siempre que la función search() encuentre la expresión regular:
---
- name: "Iterate over list"
debug: msg="Domain= {{ obj.domain }} -- Port= {{ obj.port }}"
loop: "{{ vhost_list | default([]) }}"
loop_control:
loop_var: obj
when:
- obj.domain is search(filter_test_domain)
- obj.port is search(filter_test_port)
---
En la tarea arriba reflejada, apreciamos:
- Invocación de la tarea con "lopp", la cual itera sobre los elementos de una variable "vhost_list".
- Uso de loop_control y loop_var para iterar sobre "vhost_list", y se define "obj" como nombre para la variable de cada elemento de la lista.
- Utilización de when para condicional de ejecución de la tarea. En este caso, 2 condicionales que actúan como "and".
- Cada condición del "when" evalúa una propiedad del elemento de la lista ("domain" y "port") en busca de las expresiones regulares correspondientes, definidas en las variables "filter_test_domain" y "filter_test_port".
Para continuar, mostraremos la definición de las variables a usar en el ejemplo:
---
filter_test_domain: ".*"
filter_test_port: ".*"
vhost_list:
- domain: "foo1.strsistemas.com"
port: "80"
- domain: "foo2.strsistemas.com"
port: "443"
- domain: "foo3.strsistemas.com"
port: "8080"
---
Veamos a continuación lo que hemos ganado con esto.
Si los valores de "filter_test_domain" y "filter_test_port" no se modifican, los filtros search() siempre se cumplirán y se ejecutarán en todos la tarea, iterando sobre todos los elementos de la lista.
Si modificamos el valor (ejemplo):
filter_test_port: "443"
Al ejecutar la tarea, el segundo condicional when (obj.port is search(filter_test_port)), se cumplirá unicamente para el elemento con domain="foo2.strsistemas.com" y port=443, por lo que se obviarán los elementos domain "foo1.strsistemas.com" y "foo3.strsistemas.com".
Ahora bien, la guinda la ponemos cuando cambiamos en el momento de la ejecución el valor de dichas variables con el flag "-e" de Ansible:
- ansible-playbook -i hosts_inventario stack_STR.yml -e filter_test_port="443".
- ansible-playbook -i hosts_inventario stack_STR.yml -e filter_test_port="^80" -e filter_test_domain=”(.*)strsistemas(.*)