¿Cómo mostrar la salida de un script en un playbook de Ansible?

AnsibleBeginner
Practicar Ahora

Introducción

Ansible es una poderosa herramienta de automatización que ayuda a los administradores de sistemas y desarrolladores a gestionar la infraestructura de manera eficiente. Al ejecutar scripts o comandos a través de playbooks de Ansible, a menudo es crucial capturar y mostrar su salida con fines de monitoreo y solución de problemas.

En este laboratorio práctico, aprenderá a mostrar eficazmente la salida de scripts en sus playbooks de Ansible. Comenzaremos con los conceptos básicos de la estructura de un playbook de Ansible, luego exploraremos varias técnicas para capturar y mostrar las salidas de los comandos, y finalmente cubriremos algunos métodos avanzados para formatear y filtrar los datos de salida.

Al final de este laboratorio, tendrá experiencia práctica con diferentes enfoques para manejar la salida de scripts en Ansible, lo que le permitirá crear flujos de trabajo de automatización más informativos y fáciles de depurar.

Configuración de su Primer Playbook de Ansible

Antes de que podamos explorar cómo mostrar la salida de scripts, necesitamos configurar un entorno básico de Ansible y crear nuestro primer playbook.

Instalación de Ansible

Comencemos asegurándonos de que Ansible esté instalado en nuestro sistema:

sudo apt update
sudo apt install -y ansible

Este comando instalará Ansible en su sistema Ubuntu. Una vez que la instalación esté completa, verifíquela comprobando la versión de Ansible:

ansible --version

Debería ver una salida similar a la siguiente, lo que confirma que Ansible está correctamente instalado:

ansible [core 2.12.x]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/labex/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /home/labex/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.10.x (default, Mar 14 2023, 14:50:41) [GCC 11.3.0]
  jinja version = 3.0.3
  libyaml = True

Comprensión del Inventario de Ansible

Ansible necesita saber a qué hosts conectarse. Para este laboratorio, usaremos un archivo de inventario simple que incluye solo la máquina local.

Cree un directorio para nuestro proyecto de Ansible:

mkdir -p ~/project/ansible-lab
cd ~/project/ansible-lab

Ahora, cree un archivo de inventario llamado inventory.ini:

echo "localhost ansible_connection=local" > inventory.ini

Este archivo de inventario le dice a Ansible que ejecute comandos en la máquina local sin usar SSH.

Creación de su Primer Playbook

Ahora, creemos un playbook básico de Ansible. En el WebIDE, cree un nuevo archivo llamado first_playbook.yml en el directorio ~/project/ansible-lab con el siguiente contenido:

---
- name: My First Ansible Playbook
  hosts: localhost
  gather_facts: yes

  tasks:
    - name: Display a simple message
      debug:
        msg: "Hello from Ansible!"

    - name: Display system information
      debug:
        msg: "You are running {{ ansible_distribution }} {{ ansible_distribution_version }}"

Este sencillo playbook tiene dos tareas:

  1. Mostrar un mensaje de saludo simple
  2. Mostrar información sobre el sistema operativo que está ejecutando

Ejecutemos este playbook para ver cómo funciona:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini first_playbook.yml

Debería ver una salida similar a esta:

PLAY [My First Ansible Playbook] *********************************************************

TASK [Gathering Facts] *******************************************************************
ok: [localhost]

TASK [Display a simple message] **********************************************************
ok: [localhost] => {
    "msg": "Hello from Ansible!"
}

TASK [Display system information] ********************************************************
ok: [localhost] => {
    "msg": "You are running Ubuntu 22.04"
}

PLAY RECAP *******************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Esta salida muestra:

  • El playbook se ejecutó con éxito
  • Todas las tareas se completaron con el estado "ok"
  • La salida de cada tarea de depuración se muestra

Ahora que tenemos un playbook básico funcionando, podemos explorar cómo ejecutar scripts y mostrar su salida con más detalle.

Ejecución de Scripts y Captura de Salida

Ahora que tenemos un entorno básico de Ansible configurado, exploremos cómo ejecutar scripts y capturar su salida utilizando los módulos command y shell.

La Diferencia entre los Módulos Command y Shell

Ansible proporciona dos módulos principales para ejecutar comandos:

  • command: Ejecuta un comando en un nodo remoto sin pasar por un shell, lo que significa que los operadores de shell como |, >, <, y & no funcionan.
  • shell: Ejecuta un comando a través de un shell, lo que permite el uso de operadores de shell y variables de entorno.

Creación de un Script Simple para Ejecutar

Primero, creemos un script de shell simple que podamos ejecutar con Ansible. Cree un archivo llamado system_info.sh en su directorio ~/project/ansible-lab:

cd ~/project/ansible-lab

En el WebIDE, cree el archivo con el siguiente contenido:

#!/bin/bash

echo "=== System Information ==="
echo "Hostname: $(hostname)"
echo "Kernel Version: $(uname -r)"
echo "CPU Info: $(grep 'model name' /proc/cpuinfo | head -1 | cut -d':' -f2 | xargs)"
echo "Memory Info: $(free -h | grep Mem | awk '{print $2 " total, " $3 " used, " $4 " free"}')"
echo "Disk Usage: $(df -h / | grep / | awk '{print $5 " of " $2 " used"}')"

Haga que el script sea ejecutable:

chmod +x ~/project/ansible-lab/system_info.sh

Ahora, ejecutemos este script manualmente para ver qué salida produce:

./system_info.sh

Debería ver una salida similar a:

=== System Information ===
Hostname: labex-xxxxxxxx
Kernel Version: 5.15.0-xx-generic
CPU Info: Intel(R) Xeon(R) xxxxxx
Memory Info: 8.0G total, 1.2G used, 5.8G free
Disk Usage: 15% of 50G used

Ejecución del Script con Ansible

Ahora, creemos un nuevo playbook que ejecutará este script y capturará su salida. Cree un archivo llamado script_output.yml en su directorio ~/project/ansible-lab:

---
- name: Run Script and Capture Output
  hosts: localhost
  gather_facts: no

  tasks:
    - name: Run system info script
      command: ./system_info.sh
      register: script_result

    - name: Display script output
      debug:
        msg: "{{ script_result.stdout }}"

Este playbook hace dos cosas:

  1. Ejecuta nuestro script system_info.sh utilizando el módulo command
  2. Almacena la salida en una variable llamada script_result utilizando la palabra clave register
  3. Muestra la salida capturada utilizando el módulo debug

Ejecutemos este playbook:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini script_output.yml

Debería ver una salida similar a:

PLAY [Run Script and Capture Output] ***************************************************

TASK [Run system info script] ***********************************************************
changed: [localhost]

TASK [Display script output] ************************************************************
ok: [localhost] => {
    "msg": "=== System Information ===\nHostname: labex-xxxxxxxx\nKernel Version: 5.15.0-xx-generic\nCPU Info: Intel(R) Xeon(R) xxxxxx\nMemory Info: 8.0G total, 1.2G used, 5.8G free\nDisk Usage: 15% of 50G used"
}

PLAY RECAP *****************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Observe cómo la salida aparece como una sola cadena con caracteres de nueva línea (\n). Mejoremos la visualización en la siguiente sección.

Comprensión de la Variable Register

La palabra clave register crea una variable que contiene varios atributos, no solo la salida del comando. Creemos un nuevo playbook para explorar estos atributos.

Cree un archivo llamado register_details.yml en su directorio ~/project/ansible-lab:

---
- name: Explore Register Variable
  hosts: localhost
  gather_facts: no

  tasks:
    - name: Run system info script
      command: ./system_info.sh
      register: script_result

    - name: Display all register properties
      debug:
        var: script_result

    - name: Display just stdout
      debug:
        var: script_result.stdout

    - name: Display stdout as a list of lines
      debug:
        var: script_result.stdout_lines

Ejecute este playbook:

ansible-playbook -i inventory.ini register_details.yml

Verá una salida más detallada que muestra todas las propiedades de la variable script_result, incluyendo:

  • stdout: La salida estándar como una sola cadena
  • stderr: El error estándar (si lo hay)
  • rc: El código de retorno (0 significa éxito)
  • stdout_lines: La salida estándar dividida en una lista de líneas

La última tarea es particularmente útil ya que formatea la salida como una lista de líneas, haciéndola mucho más legible.

Técnicas Avanzadas de Manejo de Salida

Ahora que entendemos los conceptos básicos de la captura de la salida de scripts, exploremos algunas técnicas más avanzadas para manejar y mostrar la salida en los playbooks de Ansible.

Manejo de Errores de Script

Al ejecutar scripts, es importante manejar correctamente las condiciones de error. Ansible marca una tarea como fallida si el comando devuelve un código de salida distinto de cero. Creemos un playbook que demuestre el manejo de errores.

Cree un archivo llamado error_handling.yml en su directorio ~/project/ansible-lab:

---
- name: Handling Script Errors
  hosts: localhost
  gather_facts: no

  tasks:
    - name: Run a command that might fail
      command: ls /nonexistent_directory
      register: cmd_result
      ignore_errors: yes

    - name: Show error message if command failed
      debug:
        msg: "Command failed with error: {{ cmd_result.stderr }}"
      when: cmd_result.rc != 0

    - name: Show success message if command succeeded
      debug:
        msg: "Command succeeded with output: {{ cmd_result.stdout }}"
      when: cmd_result.rc == 0

En este playbook:

  1. Intentamos listar un directorio que no existe
  2. Usamos ignore_errors: yes para evitar que el playbook se detenga si el comando falla
  3. Verificamos el código de retorno (cmd_result.rc) para determinar si el comando tuvo éxito o falló
  4. Mostramos mensajes apropiados según el resultado

Ejecutemos este playbook:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini error_handling.yml

Debería ver una salida similar a:

PLAY [Handling Script Errors] ***********************************************************

TASK [Run a command that might fail] ***************************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["ls", "/nonexistent_directory"], "delta": "0:00:00.003398", "end": "2023-09-15 12:34:56.789012", "msg": "non-zero return code", "rc": 2, "start": "2023-09-15 12:34:56.785614", "stderr": "ls: cannot access '/nonexistent_directory': No such file or directory", "stderr_lines": ["ls: cannot access '/nonexistent_directory': No such file or directory"], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Show error message if command failed] *********************************************
ok: [localhost] => {
    "msg": "Command failed with error: ls: cannot access '/nonexistent_directory': No such file or directory"
}

TASK [Show success message if command succeeded] ****************************************
skipping: [localhost]

PLAY RECAP *******************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=1

Observe que, a pesar de que el comando falla, el playbook continúa ejecutándose y muestra el mensaje de error.

Formateo de Salida Multilínea

Creemos un script más complejo que genere una salida multilínea, luego formateémosla correctamente en nuestro playbook.

Cree un archivo llamado package_info.sh en su directorio ~/project/ansible-lab:

#!/bin/bash

echo "TOP 5 LARGEST INSTALLED PACKAGES"
echo "================================"
dpkg-query -W -f='${Installed-Size}\t${Package}\n' | sort -n -r | head -5

Hágalo ejecutable:

chmod +x ~/project/ansible-lab/package_info.sh

Ahora cree un playbook para ejecutar este script y formatear la salida correctamente. Cree un archivo llamado formatted_output.yml:

---
- name: Format Script Output
  hosts: localhost
  gather_facts: no

  tasks:
    - name: Run package info script
      shell: ./package_info.sh
      register: pkg_info

    - name: Display raw output
      debug:
        msg: "{{ pkg_info.stdout }}"

    - name: Display formatted output with a title
      debug:
        msg: |
          Below is the package information:
          --------------------------------
          {% for line in pkg_info.stdout_lines %}
          {{ line }}
          {% endfor %}
          --------------------------------
          Total lines: {{ pkg_info.stdout_lines | length }}

Este playbook:

  1. Ejecuta nuestro script package_info.sh
  2. Muestra la salida sin formato
  3. Usa una plantilla Jinja2 para formatear la salida con un título y un pie de página

Ejecutémoslo:

ansible-playbook -i inventory.ini formatted_output.yml

Debería ver una salida bien formateada:

PLAY [Format Script Output] ***********************************************************

TASK [Run package info script] ********************************************************
changed: [localhost]

TASK [Display raw output] *************************************************************
ok: [localhost] => {
    "msg": "TOP 5 LARGEST INSTALLED PACKAGES\n================================\n112233\tsome-large-package\n99887\tanother-package\n...\n"
}

TASK [Display formatted output with a title] ******************************************
ok: [localhost] => {
    "msg": "Below is the package information:\n--------------------------------\nTOP 5 LARGEST INSTALLED PACKAGES\n================================\n112233\tsome-large-package\n99887\tanother-package\n...\n--------------------------------\nTotal lines: 7"
}

PLAY RECAP ****************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Visualización Condicional de Salida

A veces, solo desea mostrar la salida bajo ciertas condiciones, como cuando se ejecuta en modo detallado. Veamos cómo hacer esto.

Cree un archivo llamado conditional_output.yml en su directorio ~/project/ansible-lab:

---
- name: Conditional Output Display
  hosts: localhost
  gather_facts: yes

  tasks:
    - name: Get disk usage information
      shell: df -h
      register: disk_info

    - name: Display disk usage (always shown)
      debug:
        msg: "{{ disk_info.stdout_lines[0:2] }}"

    - name: Display detailed disk usage (only in verbose mode)
      debug:
        msg: "{{ disk_info.stdout_lines }}"
        verbosity: 1

En este playbook:

  1. Capturamos la información de uso del disco
  2. Siempre mostramos un resumen (las dos primeras líneas)
  3. Mostramos los detalles completos solo cuando se ejecuta en modo detallado (con la bandera -v)

Ejecutémoslo primero en modo normal:

ansible-playbook -i inventory.ini conditional_output.yml

Ahora, ejecutémoslo en modo detallado:

ansible-playbook -i inventory.ini conditional_output.yml -v

Notará que en modo detallado, obtiene la información completa del uso del disco, mientras que en modo normal, solo ve el resumen.

Esta técnica es útil para ocultar la salida potencialmente sensible o abrumadora de forma predeterminada, a la vez que la hace disponible cuando es necesaria para la depuración.

Resumen

En este laboratorio, ha aprendido varias técnicas para capturar y mostrar la salida de scripts en los playbooks de Ansible:

  1. Configuró un entorno básico de Ansible y creó su primer playbook
  2. Aprendió a ejecutar scripts utilizando los módulos command y shell
  3. Capturó la salida del script utilizando la directiva register
  4. Mostró la salida utilizando el módulo debug en diferentes formatos
  5. Manejó las condiciones de error e implementó la visualización condicional de la salida
  6. Aplicó técnicas de formato para que la salida sea más legible

Estas habilidades le permiten crear playbooks de Ansible más informativos que proporcionen comentarios claros durante la ejecución. Esta visibilidad es crucial tanto para monitorear los procesos automatizados como para solucionar problemas cuando surgen.

Al aprovechar estas técnicas de manejo de salida, puede construir flujos de trabajo de automatización más robustos que no solo realizan tareas, sino que también comunican claramente lo que está sucediendo en cada paso. Esto hace que su automatización sea más transparente y fácil de mantener, especialmente cuando se trabaja en entornos de equipo o con infraestructuras complejas.