Introducción
El editor de Field Notes tiene dos estaciones fotográficas con selecciones diferentes por procesar. Una hoja de contacto y su lista correspondiente permiten seleccionar las fotografías, pero iniciar un trabajo en segundo plano o encontrar un archivo antiguo del mismo nombre no equivale a una entrega terminada. Crea un ejemplo pequeño y utiliza Ansible para esperar y recoger los resultados actuales reales de cada estación.
Necesitas navegación básica por el terminal y práctica con un inventario pequeño de Ansible, variables y tareas command/file. El acceso SSH, las fotografías con licencia y la herramienta de imágenes están preparados. Este laboratorio explica las listas de argumentos, los bucles, los identificadores por host, las comprobaciones de finalización y los destinos de fetch antes de utilizarlos. La duración es una estimación del autor que incluye lectura y correcciones habituales, no una medición del tiempo de principiantes reales.
Crea una hoja de contacto pequeña
En este paso ejecutarás un pequeño trabajo fotográfico mediante Ansible, recogerás su imagen y lista reales y utilizarás el resultado como referencia para el trabajo mayor.
Abre la pestaña Photo Desk. En A previous walk, pulsa Open contact sheet. Una hoja de contacto reúne versiones pequeñas de varias fotografías en una página para comparar imágenes sin abrir todos los originales. Este ejemplo histórico lleva la etiqueta previous-walk: es una referencia, no el trabajo terminado de hoy. En una pantalla estrecha, desplázate dentro del marco de la imagen para leer los nombres. Pulsa Back to photo desk para volver a la página en la misma pestaña.
La historia y la asignación de estaciones son ficticias. Las fotografías de paisajes forman una colección didáctica con licencia; Photo credits enumera sus fotógrafos y fuentes reales. La herramienta preparada lee archivos JPEG reales y crea una hoja PNG y una lista CSV. Automatizarás esa herramienta sin escribir código de procesamiento de imágenes.
Cambia a la pestaña Controller. Comienza en /home/labex/project. El controlador ejecuta Ansible. Sus dos destinos SSH son contenedores que comparten el núcleo de esta VM de prácticas, no dos VM independientes. Cada destino tiene sus propios archivos aunque las rutas se llamen igual.
pwd muestra el directorio actual y cat muestra un archivo. Lee el inventario y las variables preparados:
pwd
cat inventory.yml
cat group_vars/all.yml
cat host_vars/camera-north.yml
cat host_vars/camera-south.yml
El grupo cameras del inventario contiene camera-north y camera-south con su configuración SSH real. El ansible.cfg del proyecto selecciona este inventory.yml y la confianza de hosts preparada. El inventory.ini original de la imagen no es el inventario del curso. Conserva la configuración, el inventario y los archivos de variables.
Las llaves de estilo JSON de estos archivos son YAML válido. group_vars/all.yml aporta variables compartidas: input_root es /srv/photo-sheets/batches, output_root es /srv/photo-sheets/rendered y collection_root es el directorio collected del controlador, junto al playbook. Un archivo en host_vars se aplica al host de su nombre. Aquí, photo_batch selecciona morning-walk en la estación norte y afternoon-walk en la sur. Las selecciones de hoy difieren en contenido y tamaño.
Utiliza primero la entrada sample preparada con dos fotos en la estación norte. El módulo command de Ansible ejecuta el programa preparado en ese destino SSH. Su lista argv pasa un argumento por elemento, sin pedir a un shell que interprete una cadena de comandos. La opción --input elige el directorio de origen, --output el de resultados y --station identifica la estación en el resultado. No se edita ninguna foto original.
{{ variable }} inserta un valor de Ansible. inventory_hostname es el nombre del host actual en el inventario; playbook_dir es el directorio del playbook en el controlador. gather_facts: false omite los datos de máquina que este trabajo no necesita.
El módulo fetch transfiere un archivo desde el destino SSH al controlador, en sentido inverso a copy. Un loop lo repite para los dos nombres indicados; {{ item }} es el nombre actual. De forma predeterminada, fetch conserva un árbol de directorios con el host y la ruta de origen. Aquí, flat: true elimina la ruta de origen, por lo que el destino incluye explícitamente inventory_hostname. Así se separan archivos del mismo nombre procedentes de distintas estaciones. fetch crea los directorios de destino necesarios.
Crea sample.yml. cat > filename escribe un archivo y reemplaza cualquier archivo existente del mismo nombre. <<'YAML' inicia un here-document: pega hasta el YAML final en su propia línea. El delimitador entre comillas conserva literalmente el texto pegado; Ansible procesa las expresiones al ejecutar el playbook. La sangría YAML usa espacios:
cat > sample.yml <<'YAML'
- name: Build a small selection before scheduling both workstations
hosts: camera-north
gather_facts: false
tasks:
- name: Build the two-photo practice sheet
ansible.builtin.command:
argv:
- /usr/local/bin/photo-sheet
- --input
- "{{ input_root }}/sample"
- --output
- "{{ output_root }}/sample"
- --station
- "{{ inventory_hostname }}"
- name: Collect the practice image and file list
ansible.builtin.fetch:
src: "{{ output_root }}/sample/{{ item }}"
dest: "{{ playbook_dir }}/sample/{{ inventory_hostname }}/"
flat: true
loop:
- contact-sheet.png
- files.csv
YAML
Comprueba la sintaxis antes de ejecutar el trabajo. --syntax-check revisa la estructura del playbook; no procesa fotografías:
ansible-playbook sample.yml --syntax-check
Si hay un error de escritura o sangría, vuelve a copiar el playbook completo y repite la comprobación. Ahora ejecútalo:
ansible-playbook sample.yml
La tarea de imagen es síncrona por defecto: Ansible espera a que termine el programa antes de recoger archivos. El resumen debe mostrar failed=0 y unreachable=0. Un trabajo fotográfico realiza procesamiento y puede informar de changed; repetir un procesamiento de imágenes no garantiza ausencia de cambios.
Lee la lista recogida:
cat sample/camera-north/files.csv
La cabecera es station,batch,position,filename,sha256,width,height. Debe haber dos filas para camera-north y sample, en el mismo orden de izquierda a derecha que las imágenes. Los archivos se llaman frame-01.jpg y frame-02.jpg. SHA256 es una huella del contenido de cada archivo original; no necesitas memorizar los valores largos. Las dimensiones describen la fotografía original, no su pequeña vista previa.
Cambia a Photo Desk y recarga. En Small practice sheet, usa Open contact sheet para examinar tu resultado real de dos fotos. Usa Back to photo desk y después Download contact sheet (PNG) y Download file list (CSV) en esa tarjeta. Abre la imagen y el CSV descargados: ambas fotos, los nombres y el orden de filas deben coincidir. El navegador puede añadir un número al nombre de una descarga repetida.
La tarjeta siguiente muestra la imagen real de dos fotos y ambos enlaces de descarga. Comprueba la estación y la etiqueta sample antes de utilizar los archivos.

Si un comando indica “No such file or directory”, ls enumera el directorio actual para comprobar la escritura. Usa cd /home/labex/project para volver al espacio de trabajo y repite el comando adecuado. Conserva todas las fotos originales, las referencias históricas, las notas del editor y la configuración preparada. Las selecciones actuales mayores todavía no se han generado.
Espera cada trabajo antes de recoger archivos
En este paso iniciarás las selecciones actuales de ambas estaciones, esperarás el resultado correcto de cada host y recogerás los archivos correspondientes sin colisiones.
Permanece en Controller, en /home/labex/project. El grupo cameras selecciona ambos destinos; el photo_batch de cada uno sigue eligiendo su entrada preparada. Usa estas variables para que el mismo flujo admita otros miembros, lotes y directorios sin fijar los nombres de las dos estaciones ni sus rutas de entrada.
Una tarea síncrona espera antes de que Ansible avance a la siguiente. Con async: 120, Ansible concede a la operación remota un presupuesto de ejecución de 120 segundos. poll: 0 la inicia sin esperar a su finalización en esa tarea. Iniciarla correctamente no significa procesarla correctamente: el trabajo remoto todavía puede terminar con un error.
register: photo_job guarda el resultado del inicio por separado para cada host del inventario. Su ansible_job_id identifica ese trabajo en ese host. Un JID de una estación no sirve para consultar el resultado en la otra. El módulo debug imprime las observaciones guardadas para ver las distintas asignaciones.
async_status pregunta al mismo host por su JID y guarda cada respuesta en photo_result. until: photo_result is finished repite la consulta hasta que el indicador nativo de finalización sea verdadero; retries: 60 limita los reintentos y delay: 1 espera un segundo entre ellos. Se reintenta la consulta de estado, no se crean trabajos fotográficos nuevos. SSH y el procesamiento añaden tiempo real. Un lote pequeño puede haber terminado cuando llega la primera consulta; este ejercicio no añade retrasos artificiales.
Terminar no basta para demostrar éxito. El rc del programa remoto es su estado de salida: cero significa éxito. De forma predeterminada, async_status propaga como fallo un resultado distinto de cero. La aserción assert adicional explicita el requisito de salida cero; no convierte un error en éxito. La salida de depuración siguiente permite leer los valores reales de finished y rc antes de recoger archivos. Un flujo equivalente puede confiar en la propagación nativa del fallo sin esa aserción adicional.
Ambos hosts generan los mismos nombres, contact-sheet.png y files.csv, en sus propios sistemas de archivos. Recógelos solo tras un éxito y guarda cada pareja en su directorio de host de inventario en el controlador. Photo Desk puede leer tanto esta disposición plana explícita como la jerarquía predeterminada de host y ruta de origen dentro de la carpeta de la estación. Nunca completa una colección ausente con archivos de referencia.
Escribe process-photos.yml con el mismo here-document entre comillas del paso anterior:
cat > process-photos.yml <<'YAML'
- name: Build and collect each workstation's current photo selection
hosts: cameras
gather_facts: false
tasks:
- name: Start this workstation's contact sheet
ansible.builtin.command:
argv:
- /usr/local/bin/photo-sheet
- --input
- "{{ input_root }}/{{ photo_batch }}"
- --output
- "{{ output_root }}"
- --station
- "{{ inventory_hostname }}"
async: 120
poll: 0
register: photo_job
- name: Show the job assigned to this workstation
ansible.builtin.debug:
msg:
workstation: "{{ inventory_hostname }}"
job: "{{ photo_job.ansible_job_id }}"
- name: Wait for this workstation's own job
ansible.builtin.async_status:
jid: "{{ photo_job.ansible_job_id }}"
register: photo_result
until: photo_result is finished
retries: 60
delay: 1
- name: Require successful processing before collecting
ansible.builtin.assert:
that: photo_result.rc == 0
- name: Read the actual completion result
ansible.builtin.debug:
msg:
workstation: "{{ inventory_hostname }}"
finished: "{{ photo_result.finished }}"
rc: "{{ photo_result.rc }}"
- name: Collect the image and its file list by workstation
ansible.builtin.fetch:
src: "{{ output_root }}/{{ item }}"
dest: "{{ collection_root }}/{{ inventory_hostname }}/"
flat: true
loop:
- contact-sheet.png
- files.csv
YAML
Comprueba la sintaxis:
ansible-playbook process-photos.yml --syntax-check
Inicia el trabajo real:
ansible-playbook process-photos.yml
Cada estación debe mostrar su propio identificador y después finished: true y rc: 0 en el mensaje de finalización. El resumen final debe indicar failed=0 y unreachable=0 para ambos hosts. La selección norte tiene tres fotos y la sur seis. Los trabajos reales pueden terminar en momentos diferentes. Esta ejecución demuestra planificación asíncrona real, no garantiza el rendimiento de cargas mayores.
Lee ambas listas recogidas:
cat collected/camera-north/files.csv
cat collected/camera-south/files.csv
Los campos station y batch deben corresponder a la asignación de cada host. Nombres iguales no implican fotografías originales iguales. La posición, el nombre, la huella y las dimensiones de cada fila describen una imagen de entrada real; la hoja utiliza ese mismo orden.
Cambia a Photo Desk y recarga. En Current collections, abre el enlace Open contact sheet de cada estación. Examina a tamaño completo las hojas de tres y seis fotos y usa Back to photo desk entre ellas. Descarga el PNG y CSV de cada estación, abre ambos archivos y confirma la correspondencia de imágenes y filas completas. La referencia y tu pequeña colección de práctica deben seguir disponibles.
Compara las etiquetas de host y lote de ambas tarjetas. La hoja norte tiene una fila de tres imágenes; la sur, dos filas de tres con selecciones de origen distintas.

Un trabajo terminado todavía puede fallar
Vuelve a Controller. Selecciona ahora deliberadamente una entrada inexistente solo en la estación norte. --limit camera-north limita la invocación a ese host del inventario. --extra-vars photo_batch=missing sobrescribe la variable del lote únicamente en esta invocación; no edita el archivo preparado. El trabajo se iniciará y después informará de un procesamiento terminado con fallo. Los archivos correctos existentes no deben interpretarse como un resultado nuevo satisfactorio.
Se espera que este comando falle:
ansible-playbook process-photos.yml --limit camera-north --extra-vars photo_batch=missing
Imprime inmediatamente el estado del comando anterior. $? guarda el estado de salida del último comando y echo lo imprime. Ejecutar otro comando antes sustituiría ese valor:
echo $?
Espera un estado distinto de cero de la CLI de Ansible, normalmente 2. En el mensaje de fallo, el resultado remoto está terminado, el rc del programa es 2 y el error nombra /srv/photo-sheets/batches/missing. El estado de la CLI y el del programa remoto son valores distintos; aquí coinciden en 2. Un trabajo terminado con fallo no es una recogida correcta. La tarea fetch no debe ejecutarse para este intento fallido. La estación sur queda fuera de esta invocación.
Corrige la selección volviendo al valor preparado y ejecuta de nuevo únicamente la estación norte:
ansible-playbook process-photos.yml --limit camera-north
La ejecución debe funcionar y recoger el resultado real de morning-walk. La colección sur sigue disponible desde su trabajo correcto. Vuelve a Photo Desk, recarga y descarga de nuevo el PNG y CSV del norte para confirmar las tres fotos correctas. Mantén reutilizable process-photos.yml, conserva las fotos originales y los resultados históricos y protege las notas del editor.
Para un error habitual de YAML o de nombre, utiliza ls, la recuperación del directorio y la corrección del bloque completo del paso anterior, y repite la comprobación sintáctica. No reinicies la VM ni modifiques una foto original para ocultar un error de entrada. Repetir una ejecución correcta vuelve a procesar las imágenes; una suma de comprobación fetch sin cambios no significa que no hubiera procesamiento.
Ansible deja una caché de estado después de poll: 0. Tras inspeccionar un trabajo terminado, async_status con mode: cleanup y el jid de ese host puede eliminar su entrada. Aquí es mantenimiento opcional. Limpiar la caché no cancela un trabajo en ejecución ni elimina la hoja de contacto.
Resumen
Has creado una pequeña hoja de contacto real y después has utilizado Ansible para iniciar y esperar dos trabajos asíncronos asociados a sus respectivos hosts. Has recogido cada pareja completa de imagen/CSV por estación, distinguido un fallo terminado de un éxito y corregido una entrada inexistente repitiendo únicamente el host afectado.



