Ejecutar comandos de mantenimiento y manejar fallos

AnsibleBeginner
Practicar Ahora

Introducción

Los programas de mantenimiento existentes necesitan una integración cuidadosa con la automatización declarativa. Distinguirás command, shell y script, pasarás de forma segura una ruta con espacios, clasificarás resultados de solo lectura y protegerás la ejecución según una salida real del script. Una tubería y recibos delegados del controlador harán observables la ubicación de ejecución y la propagación de fallos. También provocarás un fallo real por una entrada ausente y te recuperarás de él.

Ejecutar un comando directo con argumentos seguros

En este paso, inspeccionarás una entrada administrada con command, conservarás una ruta que contiene un espacio y clasificarás correctamente la operación de solo lectura.

Prefiere módulos declarativos como copy y file para administrar estados. Command, shell y script son útiles al integrar un ejecutable o programa de mantenimiento existente. command ejecuta un programa administrado sin una shell, por lo que no se interpretan las tuberías ni las redirecciones. Su forma argv mantiene separado cada argumento, incluida una ruta con espacios.

Cada destino preparado tiene /home/automation/maintenance input.txt con una línea de datos aprobados y un requisito previo maintenance-ready. El controlador proporciona un script existente bajo scripts/ para usarlo después. Conserva esas entradas.

Entra en el proyecto del controlador:

cd /home/labex/project

Crea un playbook usando argv. La ruta con un espacio es una entrada de lista YAML. register captura el resultado del ejecutable, incluido stdout y su código numérico de retorno rc. Cero normalmente significa éxito; un código de retorno del comando distinto de cero normalmente hace fallar la tarea.

cat > maintenance.yml <<'YAML'
- name: Inspect maintenance inputs safely
  hosts: all
  gather_facts: false
  tasks:
    - name: Read the managed input with a space-safe argument list
      ansible.builtin.command:
        argv:
          - /usr/bin/cat
          - /home/automation/maintenance input.txt
      register: input_result
      changed_when: false

    - name: Retain the inspected input in a managed record
      ansible.builtin.copy:
        dest: /home/automation/command-result.txt
        content: "{{ input_result.stdout }}\n"
        mode: "0640"
YAML

El comando cat es de solo lectura, por lo que changed_when false clasifica su resultado como sin cambios. Este ajuste cambia el informe, no lo que realmente hace un comando; no debe usarse para ocultar cambios de un programa que modifica el estado. La tarea copy consume el stdout devuelto y escribe un registro administrado real con un salto de línea final explícito.

Comprueba y ejecuta el playbook:

ansible-playbook maintenance.yml --syntax-check
ansible-playbook maintenance.yml

Ambas tareas de lectura deberían informar ok, mientras que la primera copia del registro cambia cada destino. Inspecciona los registros reales:

ansible all -m ansible.builtin.command -a 'cat /home/automation/command-result.txt'

Cada uno debería contener Approved maintenance data. En esta versión de core, command puede expandir argumentos de variables de entorno mediante Python aunque no invoque una shell. Usa expand_argument_vars false cuando se necesiten argumentos literales con prefijo de dólar; no es necesario para esta ruta fija.

Transferir un script del controlador y condicionar su ejecución a su salida real

En este paso, invocarás el script de mantenimiento proporcionado del controlador en ambos destinos y evitarás ejecuciones repetidas innecesarias.

Inspecciona el programa proporcionado antes de ejecutarlo:

cat scripts/host-maintenance.sh

El programa comprueba la entrada maintenance-ready del destino antes de crear cualquier salida. Si falta la preparación, imprime un error y termina con 42. En caso contrario, escribe un informe protegido que contiene el nombre de host real del destino y su argumento de etiqueta. Su pequeña implementación de shell es una entrada de integración existente; usa módulos declarativos para la configuración normal de archivos.

El módulo script transfiere un origen del controlador al destino y lo ejecuta allí. Esto difiere de command, cuyo ejecutable debe existir ya en el nodo administrado. La etiqueta entrecomillada siguiente sigue siendo un argumento con un espacio. La ruta creates se comprueba en el destino administrado: cuando ese informe ya existe, se omite el script.

Añade la tarea del script:

cat >> maintenance.yml <<'YAML'

    - name: Run the supplied controller script once per target report
      ansible.builtin.script:
        cmd: /home/labex/project/scripts/host-maintenance.sh 'Maintenance checked'
        creates: /home/automation/maintenance-run/report.txt
      register: maintenance_result
      failed_when: maintenance_result.rc is defined and maintenance_result.rc != 0
YAML

El rc del resultado registrado controla failed_when. La condición is defined contempla un resultado que no incluye un código de retorno de ejecución. No uses ignore_errors ni failed_when false para convertir un fallo real del script en un éxito aparente.

Ejecuta el playbook ampliado:

ansible-playbook maintenance.yml

Ambos destinos deberían crear informes. Lee las salidas reales:

ansible all -m ansible.builtin.command -a 'cat /home/automation/maintenance-run/report.txt'

Cada una debería contener su propio nombre de host y label=Maintenance checked. Repite el mismo playbook:

ansible-playbook maintenance.yml

El script debería omitirse porque su informe de destino existe, y ambos resúmenes deberían informar changed=0 sin destinos fallidos ni inaccesibles. Creates es una condición de existencia, no un motor de reconciliación de contenido: no repara un informe existente corrupto ni lo regenera automáticamente cuando cambian las entradas del script.

Propagar fallos y delegar recibos del controlador

En este paso, usarás shell únicamente para una tubería real, guardarás recibos distintos del controlador y ejercitarás un fallo visible del script sin suprimirlo.

Una tubería conecta la salida de un comando con la entrada de otro y, por tanto, necesita shell. pipefail de Bash hace que un comando fallido de una tubería produzca un fallo en lugar de permitir que un comando final wc correcto lo oculte. La tubería siguiente solo cuenta líneas de entrada; changed_when false es apropiado porque lee sin cambiar el estado del destino. failed_when también rechaza un recuento de líneas inesperado.

Crea un directorio de recibos en el controlador. Cada copia delegada usará el nombre original del inventario en un archivo separado, evitando que los hosts sobrescriban un recibo compartido:

mkdir -p receipts

Añade las tareas de tubería y recibos. delegate_to localhost ejecuta únicamente la tarea de recibo en el controlador; inventory_hostname sigue identificando el destino original. become false mantiene la creación de archivos del controlador bajo la cuenta normal labex.

cat >> maintenance.yml <<'YAML'

    - name: Count managed input lines with explicit pipeline failure propagation
      ansible.builtin.shell: |
        set -o pipefail
        cat '/home/automation/maintenance input.txt' | wc -l
      args:
        executable: /bin/bash
      register: line_result
      changed_when: false
      failed_when: line_result.rc != 0 or (line_result.stdout | trim) != '1'

    - name: Store a per-target receipt on the controller
      ansible.builtin.copy:
        dest: "/home/labex/project/receipts/{{ inventory_hostname }}.txt"
        content: |
          host={{ inventory_hostname }}
          input_lines={{ line_result.stdout | trim }}
        mode: "0640"
      delegate_to: localhost
      become: false
YAML

Ejecuta el flujo completo de mantenimiento:

ansible-playbook maintenance.yml

Inspecciona ambos recibos del controlador. Cada uno debe identificar su propio host original e input_lines=1:

cat receipts/web1.txt receipts/web2.txt

Ahora ejercita la rama real de fallo del script en web1. Elimina su informe condicionado y la entrada de preparación con el módulo file; estos son cambios temporales intencionados dentro del nodo de formación:

ansible web1 -m ansible.builtin.file -a 'path=/home/automation/maintenance-run/report.txt state=absent'
ansible web1 -m ansible.builtin.file -a 'path=/home/automation/maintenance-ready state=absent'

La siguiente ejecución debería hacer fallar la tarea del script en web1 con rc 42 y el mensaje de entrada ausente. || permite que el flujo del Terminal continúe después de esta salida esperada distinta de cero del playbook; no cambia la clasificación de fallo de Ansible:

ansible-playbook maintenance.yml || printf 'Expected missing-readiness failure\n'

Inspecciona la ruta del informe; web1 no debe tener un informe de éxito fabricado después del script fallido:

ansible web1 -m ansible.builtin.stat -a 'path=/home/automation/maintenance-run/report.txt'

Restablece la entrada de preparación desde un origen exacto del controlador y vuelve a ejecutar el flujo completo:

printf 'Ready\n' > ready.txt
ansible web1 -m ansible.builtin.copy -a 'src=/home/labex/project/ready.txt dest=/home/automation/maintenance-ready mode=0644'
ansible-playbook maintenance.yml

Web1 debería regenerar su informe correctamente. La repetición final no debería tener cambios, destinos inaccesibles ni fallos:

ansible-playbook maintenance.yml

Las inspecciones de solo lectura siguen ejecutándose, el script condicionado omite los informes existentes y cada recibo delegado permanece en el controlador. Esto conserva las señales reales de error y mantiene estable la ejecución repetida correcta.

Resumen

Has usado argv para la ejecución directa administrada, script para transferir programas del controlador al destino y shell únicamente para una tubería real con propagación explícita de fallos. Los informes de cambios de solo lectura, una condición creates real y códigos de salida no suprimidos mantuvieron significativos los resultados. Los recibos delegados por host demostraron la ejecución en el controlador y el ejercicio de entrada ausente terminó con un flujo restablecido y estable.