sábado, 31 de agosto de 2019

ANSIBLE - Ejecución condicional de tareas.

En el último post sobre Ansible vimos como usar bucles para repetir una tarea varias veces en el mismo host.

En ocasiones nos encontraremos con tareas que, o bien no queremos que se ejecuten en un host o bien deben ejecutarse de forma diferente en función de alguna característica del host. Por tanto, en este post, veremos cómo controlar la ejecución de tareas mediante el uso de condicionales.

En general, podemos imaginar muchas situaciones en las que puede ser necesario usar condicionales, como por ejemplo configurar una regla de firewall si un determinado servicio está corriendo, lanzar un comando si un interfaz de red tiene una dirección IP de un rango determinado o instalar un paquete de software en un conjunto de hosts basándonos en la versión del sistema operativo.

Como siempre la forma más sencilla de entenderlo es viendo un ejemplo, así que empecemos con uno simple como crear un fichero en función del sistema operativo del host. Lo importante de este ejemplo es que es necesario obtener los facts de nuestros hosts para poder realizar las comparaciones de manera correcta. Veamos con un ejemplo a lo que me refiero:

Uso de una condición simple.
Al ejecutar este playbook recibimos el siguuiente mensaje de error:

Error en la ejecución de la tarea.
Como vemos en el mensaje de error, ansible indica que no hay un atributo ansible_os_family con el que realizar la comparación y, debido a esto, se produce el fallo de ejecución. Esto es debido a que he especificado que no quiero que se recojan los facts de los hosts sobre los que se ejecuta la tarea, con la línea gather_facts: no, y por tanto ansible no dispondrá de la información contenida en dichas variables. Si cambiamos el playbook para que se recojan los facts de todos los hosts y lo volvemos a ejecutar, ahora el resultado será correcto:

Obteniendo los facts de los hosts la condición se evalúa correctamente.
Por tanto, la primera conclusión que sacamos es que, para usar condiciones basadas en características de los hosts será necesario obtener los facts de los mismos durante la ejecución del playbook o bien tenerlos cacheados, si queremos evitar tener que obtenerlos cada vez que ejecutemos nuestros playbooks.

Un punto interesante a tener en cuenta es que podemos hacer referencia a estas variables de host de dos formas, usando hostvars o bien usando ansible_facts:
  • Mediante el uso de hostvars podemos hacer referencia a una variable de cualquier host, no solamente del host sobre el que se está ejecutando la tarea. Para esto es necesario que, o bien ya se hayan recogido los facts del host referenciado o realicemos caching de facts.
  • Mediante ansible_facts solo haremos referencia a las variables del host sobre el cual se está ejecutando la tarea en ese momento.
En resumen, cualquiera de las dos opciones siguientes son equivalentes para nuestro ejemplo:

Opciones para usar variables de host en condicionales.
Por comodidad y si no vamos a hacer referencia a variables de otros hosts, lo mejor es usar la segunda forma en la que solo usaremos las variables del host en el que estamos ejecutando la tarea pero, en ambos casos, necesitaremos especificar la opción gather_facts a yes.

Este ejemplo tan sencillo nos ha permitido ver cómo podemos usar una condición muy simple, si ahora nos complicamos un poco más veremos que podemos usar operadores lógicos para hacer condiciones más complejas. Siguiendo con el ejemplo anterior, vamos a crear el fichero solo en aquellos hosts CentOS cuya versión sea superior a la 6, con lo cual la condición pasará a ser:

Uso de operadores lógicos en condiciones.
Y al lanzar el playbook, de nuevo creará el fichero correctamente en los hosts donde se cumpla dicha condición.

¿Cúal es la salida si el host no cumple la condición?¿que salida genera Ansible en ese caso? veremos que la tarea para el host se marca como skipping, indicando así que no se ha ejecutado:

Tarea no ejecutada en un host.
En el caso anterior, he fijado en la condición que la versión del sistema operativo debe ser igual a 6 y, al no cumplirse, Ansible no la ejecuta y la marca como skipping.

Para terminar, también podemos crear condiciones basadas en los resultados de la ejecución de otras tareas de nuestros playbooks. Por ejemplo, si necesitamos crear un directorio cuando el número de ficheros contenidos en otro directorio supere un determinado número, podemos hacer algo como lo siguiente:
Condición basada en una variable registrada en otra tarea.


En este caso registramos la salida del comando en la variable num_of_files la cual, al ser un diccionario, contiene en la clave stdout el resultado del comando ejecutado. Realizando la comparación que necesitamos con el valor de esa clave, podemos controlar cuando se ejecuta la tarea de creación del directorio. Así, cuando en el directorio hay más de 8 archivos, la ejecución será:

Resultado de la ejecucióon del playbook anterior.
En resumen, podemos controlar la ejecución de tareas mediante el uso de condiciones, ya sean con variables de los hosts o bien con aquellas que generemos durante la ejecución de tareas. Estas condiciones pueden agruparse mediante operadores lógicos, permitiendo la creación de condiciones más complejas que nos permitirán controlar nuestros playbooks con detalle.