Este equipo redactor ha estado mucho tiempo investigando para darle respuesta a tu interrogante, te ofrecemos la solución y nuestro objetivo es servirte de gran apoyo.
En un libro de jugadas, es posible que desee ejecutar diferentes tareas o tener diferentes objetivos, según el valor de un hecho (datos sobre el sistema remoto), una variable o el resultado de una tarea anterior. Es posible que desee que el valor de algunas variables dependa del valor de otras variables. O tal vez desee crear grupos adicionales de hosts en función de si los hosts coinciden con otros criterios. Puede hacer todas estas cosas con condicionales.
Ansible usa Jinja2 pruebas y filtros en condicionales. Ansible admite todas las pruebas y filtros estándar, y también agrega algunos únicos.
Nota
Hay muchas opciones para controlar el flujo de ejecución en Ansible. Puede encontrar más ejemplos de condicionales admitidos en https://jinja.palletsprojects.com/en/master/templates/#comparisons.
-
Condicionales básicos con
when
- Condicionales basados en ansible_facts
- Condiciones basadas en variables registradas
- Condicionales basados en variables
- Usar condicionales en bucles
- Cargando hechos personalizados
-
Condicionales con reutilización
- Condicionales con importaciones
- Condicionales con incluye
- Condicionales con roles
-
Seleccionar variables, archivos o plantillas basadas en hechos
- Seleccionar archivos de variables basados en hechos
- Seleccionar archivos y plantillas basados en hechos
-
Hechos de uso común
- ansible_facts[‘distribution’]
- ansible_facts[‘distribution_major_version’]
- ansible_facts[‘os_family’]
Condicionales básicos con when
La declaración condicional más simple se aplica a una sola tarea. Cree la tarea, luego agregue un when
declaración que aplica una prueba. los when
cláusula es una expresión Jinja2 en bruto sin llaves dobles (ver group_by: crea grupos de Ansible basados en hechos). Cuando ejecuta la tarea o el libro de jugadas, Ansible evalúa la prueba para todos los hosts. En cualquier host donde pasa la prueba (devuelve un valor de Verdadero), Ansible ejecuta esa tarea. Por ejemplo, si está instalando mysql en varias máquinas, algunas de las cuales tienen SELinux habilitado, es posible que tenga una tarea para configurar SELinux para permitir que mysql se ejecute. Solo querrá que esa tarea se ejecute en máquinas que tengan SELinux habilitado:
tasks:-name: Configure SELinux to start mysql on any port ansible.posix.seboolean:name: mysql_connect_any state:truepersistent: yes when: ansible_selinux.status == "enabled" # all variables can be used directly in conditionals without double curly braces
Condicionales basados en ansible_facts
A menudo, desea ejecutar u omitir una tarea basándose en hechos. Los hechos son atributos de hosts individuales, incluida la dirección IP, el sistema operativo, el estado de un sistema de archivos y muchos más. Con condicionales basados en hechos:
- Puede instalar un determinado paquete solo cuando el sistema operativo es una versión particular.
- Puede omitir la configuración de un firewall en hosts con direcciones IP internas.
- Puede realizar tareas de limpieza solo cuando un sistema de archivos se está llenando.
Ver Hechos de uso común para obtener una lista de hechos que aparecen con frecuencia en declaraciones condicionales. No todos los hechos existen para todos los hosts. Por ejemplo, el hecho ‘lsb_major_release’ utilizado en un ejemplo a continuación solo existe cuando el paquete lsb_release está instalado en el host de destino. Para ver qué datos están disponibles en sus sistemas, agregue una tarea de depuración a su libro de jugadas:
-name: Show facts available on the system ansible.builtin.debug:var: ansible_facts
Aquí hay una muestra condicional basada en un hecho:
tasks:-name: Shut down Debian flavored systems ansible.builtin.command: /sbin/shutdown -t now when: ansible_facts['os_family'] == "Debian"
Si tiene varias afecciones, puede agruparlas entre paréntesis:
tasks:-name: Shut down CentOS 6 and Debian 7 systems ansible.builtin.command: /sbin/shutdown -t now when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")
Puedes usar operadores logicos para combinar condiciones. Cuando tiene varias condiciones que deben ser verdaderas (es decir, una lógica and
), puede especificarlos como una lista:
tasks:-name: Shut down CentOS 6 systems ansible.builtin.command: /sbin/shutdown -t now when:- ansible_facts['distribution'] == "CentOS" - ansible_facts['distribution_major_version'] == "6"
Si un hecho o variable es una cadena y necesita ejecutar una comparación matemática en él, use un filtro para asegurarse de que Ansible lea el valor como un número entero:
tasks:-ansible.builtin.shell: echo "only on Red Hat 6, derivatives, and later" when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release']| int >= 6
Condiciones basadas en variables registradas
A menudo, en un libro de jugadas, desea ejecutar u omitir una tarea en función del resultado de una tarea anterior. Por ejemplo, es posible que desee configurar un servicio después de que se actualice mediante una tarea anterior. Para crear un condicional basado en una variable registrada:
- Registre el resultado de la tarea anterior como variable.
- Cree una prueba condicional basada en la variable registrada.
Creas el nombre de la variable registrada usando el register
palabra clave. Una variable registrada siempre contiene el estado de la tarea que la creó, así como cualquier salida que generó esa tarea. Puede utilizar variables registradas en plantillas y líneas de acción, así como en condicionales. when
declaraciones. Puede acceder al contenido de la cadena de la variable registrada utilizando variable.stdout
. Por ejemplo:
-name: Test play hosts: all tasks:-name: Register a variable ansible.builtin.shell: cat /etc/motd register: motd_contents -name: Use the variable in conditional statement ansible.builtin.shell: echo "motd contains the word hi" when: motd_contents.stdout.find('hi') !=-1
Puede utilizar los resultados registrados en el ciclo de una tarea si la variable es una lista. Si la variable no es una lista, puede convertirla en una lista, con stdout_lines
o con variable.stdout.split()
. También puede dividir las líneas por otros campos:
-name: Registered variable usage as a loop list hosts: all tasks:-name: Retrieve the list of home directories ansible.builtin.command: ls /home register: home_dirs -name: Add home dirs to the backup spooler ansible.builtin.file:path: /mnt/bkspool/ item src: /home/ item state: link loop:" home_dirs.stdout_lines "# same as loop: " home_dirs.stdout.split() "
El contenido de la cadena de una variable registrada puede estar vacío. Si desea ejecutar otra tarea solo en hosts donde la salida estándar de su variable registrada está vacía, verifique que el contenido de la cadena de la variable registrada esté vacío:
-name: check registered variable for emptiness hosts: all tasks:-name: List contents of directory ansible.builtin.command: ls mydir register: contents -name: Check contents for emptiness ansible.builtin.debug:msg:"Directory is empty"when: contents.stdout == ""
Ansible siempre registra algo en una variable registrada para cada host, incluso en hosts donde falla una tarea o Ansible omite una tarea porque no se cumple una condición. Para ejecutar una tarea de seguimiento en estos hosts, consulte la variable registrada para is skipped
(no para “indefinido” o “predeterminado”). Ver Registro de variables para más información. Aquí hay ejemplos de condicionales basados en el éxito o el fracaso de una tarea. Recuerde ignorar los errores si desea que Ansible continúe ejecutándose en un host cuando ocurre una falla:
tasks:-name: Register a variable, ignore errors and continue ansible.builtin.command: /bin/false register: result ignore_errors:true-name: Run only if the task that registered the "result" variable fails ansible.builtin.command: /bin/something when: result is failed -name: Run only if the task that registered the "result" variable succeeds ansible.builtin.command: /bin/something_else when: result is succeeded -name: Run only if the task that registered the "result" variable is skipped ansible.builtin.command: /bin/still/something_else when: result is skipped
Nota
Versiones anteriores de Ansible utilizadas success
y fail
, pero succeeded
y failed
usa el tiempo correcto. Todas estas opciones ahora son válidas.
Condicionales basados en variables
También puede crear condicionales basados en variables definidas en los libros de jugadas o el inventario. Debido a que los condicionales requieren entrada booleana (una prueba debe evaluarse como Verdadero para activar la condición), debe aplicar el | bool
filtrar a variables no booleanas, como variables de cadena con contenido como ‘sí’, ‘activado’, ‘1’ o ‘verdadero’. Puede definir variables como esta:
vars:epic:truemonumental:"yes"
Con las variables anteriores, Ansible ejecutaría una de estas tareas y omitiría la otra:
tasks:-name: Run the command if "epic" or "monumental" is true ansible.builtin.shell: echo "This certainly is epic!" when: epic or monumental | bool -name: Run the command if "epic" is false ansible.builtin.shell: echo "This certainly isn't epic!" when: not epic
Si no se ha establecido una variable requerida, puede omitir o fallar usando Jinja2 defined
prueba. Por ejemplo:
tasks:-name: Run the command if "foo" is defined ansible.builtin.shell: echo "I've got ' foo ' and am not afraid to use it!" when: foo is defined -name: Fail if "bar" is undefined ansible.builtin.fail: msg="Bailing out. This play requires 'bar'" when: bar is undefined
Esto es especialmente útil en combinación con la importación condicional de archivos vars (ver más abajo). Como muestran los ejemplos, no es necesario utilizar
utilizar variables dentro de condicionales, ya que estas ya están implícitas.
Usar condicionales en bucles
Si combina un when
declaración con un círculo, Ansible procesa la condición por separado para cada artículo. Esto es por diseño, por lo que puede ejecutar la tarea en algunos elementos del ciclo y omitirla en otros elementos. Por ejemplo:
tasks:-name: Run with items greater than 5 ansible.builtin.command: echo item loop:[0,2,4,6,8,10]when: item > 5
Si necesita omitir toda la tarea cuando la variable de bucle no está definida, use el |default
filter para proporcionar un iterador vacío. Por ejemplo, al recorrer una lista:
-name: Skip the whole task when a loop variable is undefined ansible.builtin.command: echo item loop:" mylist"when: item > 5
Puede hacer lo mismo al hacer un bucle sobre un dictado:
-name: The same as above using a dict ansible.builtin.command: echo item.key loop:"default()) "when: item.value > 5
Cargando hechos personalizados
Puede proporcionar sus propios datos, como se describe en ¿Deberías desarrollar un módulo?. Para ejecutarlos, simplemente haga una llamada a su propio módulo de recopilación de datos personalizado en la parte superior de su lista de tareas, y las variables devueltas allí serán accesibles para futuras tareas:
tasks:-name: Gather site specific fact data action: site_facts -name: Use a custom fact ansible.builtin.command: /usr/bin/thingy when: my_custom_fact_just_retrieved_from_the_remote_system == '1234'
Condicionales con reutilización
Puede utilizar condicionales con archivos de tareas, libros de jugadas o roles reutilizables. Ansible ejecuta estas declaraciones condicionales de manera diferente para la reutilización dinámica (incluye) y para la reutilización estática (importaciones). Ver Reutilización de artefactos de Ansible para obtener más información sobre la reutilización en Ansible.
Condicionales con importaciones
Cuando agrega un condicional a una declaración de importación, Ansible aplica la condición a todas las tareas dentro del archivo importado. Este comportamiento es el equivalente a Herencia de etiquetas: agregar etiquetas a varias tareas. Ansible aplica la condición a cada tarea y evalúa cada tarea por separado. Por ejemplo, puede tener un libro de jugadas llamado main.yml
y un archivo de tareas llamado other_tasks.yml
:
# all tasks within an imported file inherit the condition from the import statement# main.yml-import_tasks: other_tasks.yml # note "import"when: x is not defined # other_tasks.yml-name: Set a variable ansible.builtin.set_fact:x: foo -name: Print a variable ansible.builtin.debug:var: x
Ansible expande esto en el momento de la ejecución al equivalente de:
-name: Set a variable if not defined ansible.builtin.set_fact:x: foo when: x is not defined # this task sets a value for x-name: Do the task if "x" is not defined ansible.builtin.debug:var: x when: x is not defined # Ansible skips this task, because x is now defined
Así que si x
es inicialmente indefinido, el debug
se omitirá la tarea. Si este no es el comportamiento que desea, utilice un include_*
declaración para aplicar una condición solo a esa declaración en sí.
Puede aplicar condiciones a import_playbook
así como al otro import_*
declaraciones. Cuando usa este enfoque, Ansible devuelve un mensaje ‘omitido’ para cada tarea en cada host que no coincide con los criterios, creando una salida repetitiva. En muchos casos el módulo group_by puede ser una forma más ágil de lograr el mismo objetivo; ver Manejo de diferencias de distribución y SO.
Condicionales con incluye
Cuando usas un condicional en un include_*
declaración, la condición se aplica solo a la tarea de inclusión en sí y no a ninguna otra tarea dentro de los archivos incluidos. Para contrastar con el ejemplo usado para los condicionales en las importaciones anteriores, mire el mismo libro de jugadas y archivo de tareas, pero usando una inclusión en lugar de una importación:
# Includes let you re-use a file to define a variable when it is not already defined# main.yml-include_tasks: other_tasks.yml when: x is not defined # other_tasks.yml-name: Set a variable ansible.builtin.set_fact:x: foo -name: Print a variable ansible.builtin.debug:var: x
Ansible expande esto en el momento de la ejecución al equivalente de:
# main.yml-include_tasks: other_tasks.yml when: x is not defined # if condition is met, Ansible includes other_tasks.yml# other_tasks.yml-name: Set a variable ansible.builtin.set_fact:x: foo # no condition applied to this task, Ansible sets the value of x to foo-name: Print a variable ansible.builtin.debug:var: x # no condition applied to this task, Ansible prints the debug statement
Mediante el uso include_tasks
en lugar de import_tasks
, ambas tareas de other_tasks.yml
se ejecutará como se esperaba. Para obtener más información sobre las diferencias entre include
v import
ver Reutilización de artefactos de Ansible.
Condicionales con roles
Hay tres formas de aplicar condiciones a los roles:
- Agregue la misma condición o condiciones a todas las tareas en el rol colocando su
when
declaración bajo elroles
palabra clave. Vea el ejemplo en esta sección. - Agregue la misma condición o condiciones a todas las tareas en el rol colocando su
when
declaración en una estáticaimport_role
en su libro de jugadas. - Agregue una condición o condiciones a tareas o bloques individuales dentro del rol en sí. Este es el único enfoque que le permite seleccionar u omitir algunas tareas dentro del rol según su
when
declaración. Para seleccionar u omitir tareas dentro del rol, debe tener condiciones establecidas en tareas o bloques individuales, use la dinámicainclude_role
en su libro de jugadas y agregue la condición o condiciones a la inclusión. Cuando usa este enfoque, Ansible aplica la condición a la inclusión en sí más cualquier tarea en el rol que también tenga esewhen
declaración.
Cuando incorporas un rol en tu libro de jugadas de forma estática con el roles
palabra clave, Ansible agrega las condiciones que usted define a todas las tareas del rol. Por ejemplo:
-hosts: webservers roles:-role: debian_stock_config when: ansible_facts['os_family'] == 'Debian'
Seleccionar variables, archivos o plantillas basadas en hechos
A veces, los datos sobre un host determinan los valores que desea utilizar para determinadas variables o incluso el archivo o la plantilla que desea seleccionar para ese host. Por ejemplo, los nombres de los paquetes son diferentes en CentOS y en Debian. Los archivos de configuración para servicios comunes también son diferentes en diferentes versiones y sabores de SO. Para cargar diferentes archivos de variables, plantillas u otros archivos basados en un hecho sobre los hosts:
- nombre sus archivos vars, plantillas o archivos para que coincidan con el hecho de Ansible que los diferencia
- seleccione el archivo, plantilla o archivo vars correcto para cada host con una variable basada en ese hecho de Ansible
Ansible separa las variables de las tareas, evitando que sus libros de jugadas se conviertan en código arbitrario con condicionales anidados. Este enfoque da como resultado reglas de configuración más optimizadas y auditables porque hay menos puntos de decisión para rastrear.
Seleccionar archivos de variables basados en hechos
Puede crear un libro de jugadas que funcione en múltiples plataformas y versiones de SO con un mínimo de sintaxis colocando los valores de sus variables en archivos vars e importándolos condicionalmente. Si desea instalar Apache en algunos servidores CentOS y Debian, cree archivos de variables con claves y valores YAML. Por ejemplo:
---# for vars/RedHat.ymlapache: httpd somethingelse:42
Luego, importe esos archivos de variables en función de los datos que recopile sobre los hosts en su libro de jugadas:
----hosts: webservers remote_user: root vars_files:-"vars/common.yml"-["vars/ ansible_facts['os_family'] .yml","vars/os_defaults.yml"]tasks:-name: Make sure apache is started ansible.builtin.service:name:' apache 'state: started
Ansible recopila datos sobre los hosts en el grupo de servidores web y luego interpola la variable “ansible_facts[‘os_family’]”En una lista de nombres de archivo. Si tiene hosts con sistemas operativos Red Hat (CentOS, por ejemplo), Ansible busca ‘vars / RedHat.yml’. Si ese archivo no existe, Ansible intenta cargar ‘vars / os_defaults.yml’. Para los hosts Debian, Ansible busca primero ‘vars / Debian.yml’, antes de recurrir a ‘vars / os_defaults.yml’. Si no se encuentran archivos en la lista, Ansible genera un error.
Seleccionar archivos y plantillas basados en hechos
Puede usar el mismo enfoque cuando diferentes versiones o sabores de SO requieren diferentes archivos de configuración o plantillas. Seleccione el archivo o la plantilla adecuados en función de las variables asignadas a cada host. Este enfoque suele ser mucho más limpio que poner muchos condicionales en una sola plantilla para cubrir múltiples versiones de paquetes o sistemas operativos.
Por ejemplo, puede crear una plantilla de un archivo de configuración que sea muy diferente entre, digamos, CentOS y Debian:
-name: Template a file ansible.builtin.template:src:" item "dest: /etc/myapp/foo.conf loop:" query('first_found', 'files': myfiles, 'paths': mypaths) "vars:myfiles:-" ansible_facts['distribution'] .conf"- default.conf mypaths:['search_location_one/somedir/','/opt/other_location/somedir/']
Hechos de uso común
Los siguientes hechos de Ansible se utilizan con frecuencia en condicionales.
ansible_facts[‘distribution’]
Valores posibles (muestra, lista no completa):
Alpine Altlinux Amazon Archlinux ClearLinux Coreos CentOS Debian Fedora Gentoo Mandriva NA OpenWrt OracleLinux RedHat Slackware SLES SMGL SUSE Ubuntu VMwareESX
ansible_facts[‘distribution_major_version’]
La versión principal del sistema operativo. Por ejemplo, el valor es 16
para Ubuntu 16.04.
ansible_facts[‘os_family’]
Valores posibles (muestra, lista no completa):
AIX
Alpine
Altlinux
Archlinux
Darwin
Debian
FreeBSD
Gentoo
HP-UX
Mandrake
RedHat
SGML
Slackware
Solaris
Suse
Windows
Ver también
- Trabajar con libros de jugadas
-
Introducción a los libros de jugadas
- Roles
-
Organización del libro de jugadas por roles
- Consejos y trucos
-
Consejos y trucos para libros de jugadas
- Usando Variables
-
Todo sobre variables
- Lista de correo de usuarios
-
¿Tengo una pregunta? ¡Pasa por el grupo de google!
- irc.freenode.net
-
#ansible canal de chat de IRC
Aquí tienes las comentarios y calificaciones
Si te ha resultado útil nuestro artículo, sería de mucha ayuda si lo compartes con el resto seniors así contrubuyes a dar difusión a nuestra información.