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:

  1. Registre el resultado de la tarea anterior como variable.
  2. 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 el roles 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ática import_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ámica include_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 ese when 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:

  1. nombre sus archivos vars, plantillas o archivos para que coincidan con el hecho de Ansible que los diferencia
  2. 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