Как захватить вывод скрипта, выполненного Ansible

AnsibleAnsibleBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Introduction

Ansible is a widely-used IT automation tool that simplifies the management of complex infrastructure and application deployments. In this tutorial, we will explore how to capture the output of scripts executed through Ansible playbooks. This capability is essential for monitoring, debugging, and analyzing the results of your automation tasks. By the end of this lab, you will understand various techniques to capture and utilize script output in your Ansible workflows.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL ansible(("Ansible")) -.-> ansible/ModuleOperationsGroup(["Module Operations"]) ansible(("Ansible")) -.-> ansible/PlaybookEssentialsGroup(["Playbook Essentials"]) ansible/ModuleOperationsGroup -.-> ansible/command("Execute Commands") ansible/ModuleOperationsGroup -.-> ansible/copy("Transfer Files") ansible/ModuleOperationsGroup -.-> ansible/debug("Test Output") ansible/ModuleOperationsGroup -.-> ansible/file("Manage Files/Directories") ansible/ModuleOperationsGroup -.-> ansible/script("Run Scripts") ansible/ModuleOperationsGroup -.-> ansible/shell("Execute Shell Commands") ansible/PlaybookEssentialsGroup -.-> ansible/playbook("Execute Playbook") subgraph Lab Skills ansible/command -.-> lab-414952{{"Как захватить вывод скрипта, выполненного Ansible"}} ansible/copy -.-> lab-414952{{"Как захватить вывод скрипта, выполненного Ansible"}} ansible/debug -.-> lab-414952{{"Как захватить вывод скрипта, выполненного Ansible"}} ansible/file -.-> lab-414952{{"Как захватить вывод скрипта, выполненного Ansible"}} ansible/script -.-> lab-414952{{"Как захватить вывод скрипта, выполненного Ansible"}} ansible/shell -.-> lab-414952{{"Как захватить вывод скрипта, выполненного Ansible"}} ansible/playbook -.-> lab-414952{{"Как захватить вывод скрипта, выполненного Ansible"}} end

Setting Up Ansible Environment

Before we can start capturing script outputs with Ansible, we need to set up a basic Ansible environment. This includes creating the necessary directory structure and configuration files.

Understanding Ansible Basics

Ansible works by connecting to target hosts and pushing out small programs called modules. These modules are executed on the target hosts and removed when complete. Ansible is agent-less, which means you do not need to install any special software on the managed nodes.

Let us start by creating a project directory and the necessary Ansible files:

mkdir -p ~/project/ansible-output-demo/scripts
cd ~/project/ansible-output-demo

Now, let us create a simple inventory file. In Ansible, the inventory file defines the hosts and groups of hosts upon which commands, modules, and tasks in a playbook operate.

Create an inventory file using the code editor:

  1. Click on the "File" menu in the upper left corner of the IDE
  2. Select "New File"
  3. Save it as inventory in the ~/project/ansible-output-demo directory

Add the following content to the inventory file:

[local]
localhost ansible_connection=local

This inventory file specifies that we will be running Ansible on the local machine.

Next, let us create a simple script that will generate some output for us to capture. This script will:

  1. Print some system information
  2. Generate some standard output
  3. Generate some standard error output

Create a new file in the scripts directory named info.sh:

  1. Click on the "File" menu
  2. Select "New File"
  3. Save it as scripts/info.sh in the ~/project/ansible-output-demo directory

Add the following content to the info.sh file:

#!/bin/bash

## Print system information
echo "=== System Information ==="
echo "Hostname: $(hostname)"
echo "Date: $(date)"
echo "Kernel: $(uname -r)"
echo "Memory:"
free -h

## Generate some standard output
echo "=== Standard Output ==="
echo "This is standard output"
echo "Hello from the script!"

## Generate some standard error
echo "=== Standard Error ===" >&2
echo "This is standard error" >&2
echo "An example error message" >&2

## Exit with a specific code
exit 0

Now, let us make the script executable:

chmod +x ~/project/ansible-output-demo/scripts/info.sh

Let us run the script directly to see what output it produces:

~/project/ansible-output-demo/scripts/info.sh

You should see output containing system information, standard output messages, and standard error messages.

Now we have our basic environment set up. In the next step, we will create an Ansible playbook to execute this script and capture its output.

Basic Output Capture with Ansible

Now that we have set up our environment, let us create a simple Ansible playbook that executes our script and captures its output.

Creating a Basic Playbook

In Ansible, playbooks are YAML files that define a set of tasks to be executed on remote hosts. Let us create a playbook that runs our info.sh script and captures its output using the register keyword.

Create a new file named capture_output.yml in the ~/project/ansible-output-demo directory:

  1. Click on the "File" menu
  2. Select "New File"
  3. Save it as capture_output.yml in the ~/project/ansible-output-demo directory

Add the following content to the capture_output.yml file:

---
- name: Capture Script Output
  hosts: local
  gather_facts: no

  tasks:
    - name: Execute the info.sh script
      command: "{{ playbook_dir }}/scripts/info.sh"
      register: script_output

    - name: Display the script output
      debug:
        var: script_output.stdout

Let us examine this playbook:

  1. The playbook targets the local group defined in our inventory.
  2. The first task executes our info.sh script using the command module.
  3. The register keyword stores the output of the command in a variable named script_output.
  4. The second task uses the debug module to display the standard output (stdout) of the script.

Running the Playbook

Now let us run the playbook to see how it captures and displays the script output:

cd ~/project/ansible-output-demo
ansible-playbook -i inventory capture_output.yml

You should see output similar to the following:

PLAY [Capture Script Output] *******************************************

TASK [Execute the info.sh script] **************************************
changed: [localhost]

TASK [Display the script output] ***************************************
ok: [localhost] => {
    "script_output.stdout": "=== System Information ===\nHostname: ubuntu\nDate: Tue Oct 17 12:34:56 UTC 2023\nKernel: 5.15.0-1031-aws\nMemory:\n              total        used        free      shared  buff/cache   available\nMem:          7.7Gi       1.2Gi       5.2Gi        12Mi       1.3Gi       6.3Gi\nSwap:            0B          0B          0B\n=== Standard Output ===\nThis is standard output\nHello from the script!"
}

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

Notice that only the standard output is displayed. The standard error (stderr) output is not shown because we only asked to display script_output.stdout.

Improving Output Readability

The output is a bit difficult to read as a single string. Let us modify our playbook to display the output in a more readable format using the stdout_lines attribute, which presents the output as a list of lines.

Edit the capture_output.yml file and modify the second task as follows:

- name: Display the script output
  debug:
    var: script_output.stdout_lines

Run the playbook again:

ansible-playbook -i inventory capture_output.yml

Now the output should be more readable, with each line displayed separately:

TASK [Display the script output] ***************************************
ok: [localhost] => {
    "script_output.stdout_lines": [
        "=== System Information ===",
        "Hostname: ubuntu",
        "Date: Tue Oct 17 12:34:56 UTC 2023",
        "Kernel: 5.15.0-1031-aws",
        "Memory:",
        "              total        used        free      shared  buff/cache   available",
        "Mem:          7.7Gi       1.2Gi       5.2Gi        12Mi       1.3Gi       6.3Gi",
        "Swap:            0B          0B          0B",
        "=== Standard Output ===",
        "This is standard output",
        "Hello from the script!"
    ]
}

This format makes the output much easier to read and work with. In the next step, we will explore how to capture and display different types of output.

Capturing Different Types of Output

In the previous step, we captured and displayed the standard output of our script. However, when executing scripts, there are several types of output we might want to capture:

  1. Standard Output (stdout): The normal output of the script
  2. Standard Error (stderr): Error messages and warnings
  3. Return Code (rc): The exit status of the script (0 typically means success, non-zero values indicate errors)

Let us create a new playbook that captures and displays all three types of output.

Create a new file named capture_all_output.yml in the ~/project/ansible-output-demo directory:

  1. Click on the "File" menu
  2. Select "New File"
  3. Save it as capture_all_output.yml in the ~/project/ansible-output-demo directory

Add the following content to the capture_all_output.yml file:

---
- name: Capture All Types of Script Output
  hosts: local
  gather_facts: no

  tasks:
    - name: Execute the info.sh script
      command: "{{ playbook_dir }}/scripts/info.sh"
      register: script_output

    - name: Display standard output
      debug:
        msg: "Standard Output (stdout):"

    - name: Display stdout content
      debug:
        var: script_output.stdout_lines

    - name: Display standard error
      debug:
        msg: "Standard Error (stderr):"

    - name: Display stderr content
      debug:
        var: script_output.stderr_lines

    - name: Display return code
      debug:
        msg: "Return Code: {{ script_output.rc }}"

This playbook executes our script and then displays:

  1. The standard output using script_output.stdout_lines
  2. The standard error using script_output.stderr_lines
  3. The return code using script_output.rc

Running the Enhanced Playbook

Let us run our new playbook:

cd ~/project/ansible-output-demo
ansible-playbook -i inventory capture_all_output.yml

You should see a comprehensive display of all three types of output:

PLAY [Capture All Types of Script Output] *****************************

TASK [Execute the info.sh script] *************************************
changed: [localhost]

TASK [Display standard output] ****************************************
ok: [localhost] => {
    "msg": "Standard Output (stdout):"
}

TASK [Display stdout content] *****************************************
ok: [localhost] => {
    "script_output.stdout_lines": [
        "=== System Information ===",
        "Hostname: ubuntu",
        "Date: Tue Oct 17 12:40:22 UTC 2023",
        "Kernel: 5.15.0-1031-aws",
        "Memory:",
        "              total        used        free      shared  buff/cache   available",
        "Mem:          7.7Gi       1.2Gi       5.2Gi        12Mi       1.3Gi       6.3Gi",
        "Swap:            0B          0B          0B",
        "=== Standard Output ===",
        "This is standard output",
        "Hello from the script!"
    ]
}

TASK [Display standard error] *****************************************
ok: [localhost] => {
    "msg": "Standard Error (stderr):"
}

TASK [Display stderr content] *****************************************
ok: [localhost] => {
    "script_output.stderr_lines": [
        "=== Standard Error ===",
        "This is standard error",
        "An example error message"
    ]
}

TASK [Display return code] ********************************************
ok: [localhost] => {
    "msg": "Return Code: 0"
}

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

Now we can see all types of output from our script:

  • The standard output shows system information and our regular messages
  • The standard error shows our error messages
  • The return code is 0, indicating successful execution

Creating a Script with Errors

Let us create a script that will produce an error and return a non-zero exit code to see how Ansible handles it.

Create a new file named error.sh in the scripts directory:

  1. Click on the "File" menu
  2. Select "New File"
  3. Save it as scripts/error.sh in the ~/project/ansible-output-demo directory

Add the following content to the error.sh file:

#!/bin/bash

## Print some standard output
echo "Starting error demonstration script"
echo "This will appear in stdout"

## Print some standard error
echo "This will appear in stderr" >&2
echo "Error: Something went wrong!" >&2

## Exit with a non-zero code to indicate error
exit 1

Make the script executable:

chmod +x ~/project/ansible-output-demo/scripts/error.sh

Now let us create a playbook to execute this script and handle the error. Create a new file named handle_errors.yml:

  1. Click on the "File" menu
  2. Select "New File"
  3. Save it as handle_errors.yml in the ~/project/ansible-output-demo directory

Add the following content to the handle_errors.yml file:

---
- name: Handle Script Errors
  hosts: local
  gather_facts: no

  tasks:
    - name: Execute the error script
      command: "{{ playbook_dir }}/scripts/error.sh"
      register: script_output
      ignore_errors: yes

    - name: Display standard output
      debug:
        var: script_output.stdout_lines

    - name: Display standard error
      debug:
        var: script_output.stderr_lines

    - name: Display return code
      debug:
        msg: "Return Code: {{ script_output.rc }}"

    - name: Check if script failed
      debug:
        msg: "The script failed with return code {{ script_output.rc }}"
      when: script_output.rc != 0

Notice the addition of ignore_errors: yes which tells Ansible to continue running the playbook even if the command fails (returns a non-zero exit code).

Let us run this playbook:

ansible-playbook -i inventory handle_errors.yml

You should see output similar to the following:

PLAY [Handle Script Errors] *******************************************

TASK [Execute the error script] ***************************************
changed: [localhost]

TASK [Display standard output] ****************************************
ok: [localhost] => {
    "script_output.stdout_lines": [
        "Starting error demonstration script",
        "This will appear in stdout"
    ]
}

TASK [Display standard error] *****************************************
ok: [localhost] => {
    "script_output.stderr_lines": [
        "This will appear in stderr",
        "Error: Something went wrong!"
    ]
}

TASK [Display return code] ********************************************
ok: [localhost] => {
    "msg": "Return Code: 1"
}

TASK [Check if script failed] *****************************************
ok: [localhost] => {
    "msg": "The script failed with return code 1"
}

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

This example demonstrates how to:

  1. Capture output from a script that produces an error
  2. Continue the playbook execution despite the error
  3. Conditionally execute tasks based on the script's return code

In the next step, we will explore more advanced use cases and best practices for working with script output in Ansible.

Advanced Output Processing and Practical Use Cases

Now that we understand how to capture different types of output, let us explore some more advanced techniques for processing and utilizing script output in Ansible.

Parsing Output with Filters

Ansible provides various filters that allow you to manipulate and extract specific information from script output. In this section, we will look at some common filtering techniques.

Create a new file named parse_output.yml in the ~/project/ansible-output-demo directory:

  1. Click on the "File" menu
  2. Select "New File"
  3. Save it as parse_output.yml in the ~/project/ansible-output-demo directory

First, let us create a script that generates some structured output we can parse. Create a new file named system_stats.sh:

  1. Click on the "File" menu
  2. Select "New File"
  3. Save it as scripts/system_stats.sh in the ~/project/ansible-output-demo directory

Add the following content to the system_stats.sh file:

#!/bin/bash

## Display CPU info
echo "CPU_MODEL: $(grep 'model name' /proc/cpuinfo | head -1 | cut -d ':' -f2 | xargs)"
echo "CPU_CORES: $(grep -c 'processor' /proc/cpuinfo)"

## Display memory info in GB
mem_total=$(free -g | grep Mem | awk '{print $2}')
echo "MEMORY_GB: $mem_total"

## Display disk usage
disk_usage=$(df -h / | tail -1 | awk '{print $5}' | tr -d '%')
echo "DISK_USAGE_PCT: $disk_usage"

## Display load average
load_avg=$(uptime | awk -F'load average: ' '{print $2}' | cut -d, -f1)
echo "LOAD_AVG: $load_avg"

exit 0

Make the script executable:

chmod +x ~/project/ansible-output-demo/scripts/system_stats.sh

Now let us create a playbook that executes this script, captures its output, and parses it to extract specific information:

Add the following content to the parse_output.yml file:

---
- name: Parse Script Output
  hosts: local
  gather_facts: no

  tasks:
    - name: Execute the system_stats.sh script
      command: "{{ playbook_dir }}/scripts/system_stats.sh"
      register: stats_output

    - name: Display raw output
      debug:
        var: stats_output.stdout_lines

    - name: Parse CPU model
      set_fact:
        cpu_model: "{{ stats_output.stdout | regex_search('CPU_MODEL: (.+)', '\\1') | first }}"

    - name: Parse CPU cores
      set_fact:
        cpu_cores: "{{ stats_output.stdout | regex_search('CPU_CORES: (\\d+)', '\\1') | first }}"

    - name: Parse memory
      set_fact:
        memory_gb: "{{ stats_output.stdout | regex_search('MEMORY_GB: (\\d+)', '\\1') | first }}"

    - name: Parse disk usage
      set_fact:
        disk_usage: "{{ stats_output.stdout | regex_search('DISK_USAGE_PCT: (\\d+)', '\\1') | first }}"

    - name: Parse load average
      set_fact:
        load_avg: "{{ stats_output.stdout | regex_search('LOAD_AVG: ([0-9.]+)', '\\1') | first }}"

    - name: Display parsed information
      debug:
        msg: |
          Parsed system information:
          - CPU Model: {{ cpu_model }}
          - CPU Cores: {{ cpu_cores }}
          - Memory (GB): {{ memory_gb }}
          - Disk Usage (%): {{ disk_usage }}
          - Load Average: {{ load_avg }}

This playbook:

  1. Executes our system_stats.sh script
  2. Displays the raw output
  3. Uses the regex_search filter to extract specific pieces of information from the output
  4. Stores the extracted information in variables
  5. Displays the parsed information in a structured format

Let us run this playbook:

cd ~/project/ansible-output-demo
ansible-playbook -i inventory parse_output.yml

You should see output similar to the following:

PLAY [Parse Script Output] ********************************************

TASK [Execute the system_stats.sh script] *****************************
changed: [localhost]

TASK [Display raw output] *********************************************
ok: [localhost] => {
    "stats_output.stdout_lines": [
        "CPU_MODEL: Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz",
        "CPU_CORES: 2",
        "MEMORY_GB: 7",
        "DISK_USAGE_PCT: 58",
        "LOAD_AVG: 0.08"
    ]
}

TASK [Parse CPU model] ************************************************
ok: [localhost]

TASK [Parse CPU cores] ************************************************
ok: [localhost]

TASK [Parse memory] ***************************************************
ok: [localhost]

TASK [Parse disk usage] ***********************************************
ok: [localhost]

TASK [Parse load average] *********************************************
ok: [localhost]

TASK [Display parsed information] *************************************
ok: [localhost] => {
    "msg": "Parsed system information:\n- CPU Model: Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz\n- CPU Cores: 2\n- Memory (GB): 7\n- Disk Usage (%): 58\n- Load Average: 0.08"
}

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

Making Decisions Based on Output

One of the most powerful aspects of capturing script output is using it to make decisions in your Ansible playbooks. Let us create a playbook that demonstrates conditional execution based on script output.

Create a new file named conditional_actions.yml in the ~/project/ansible-output-demo directory:

  1. Click on the "File" menu
  2. Select "New File"
  3. Save it as conditional_actions.yml in the ~/project/ansible-output-demo directory

Add the following content:

---
- name: Conditional Actions Based on Script Output
  hosts: local
  gather_facts: no

  tasks:
    - name: Execute the system_stats.sh script
      command: "{{ playbook_dir }}/scripts/system_stats.sh"
      register: stats_output

    - name: Parse disk usage
      set_fact:
        disk_usage: "{{ stats_output.stdout | regex_search('DISK_USAGE_PCT: (\\d+)', '\\1') | first | int }}"

    - name: Parse load average
      set_fact:
        load_avg: "{{ stats_output.stdout | regex_search('LOAD_AVG: ([0-9.]+)', '\\1') | first | float }}"

    - name: Display system status
      debug:
        msg: "Current system status: Disk usage: {{ disk_usage }}%, Load average: {{ load_avg }}"

    - name: Warn about high disk usage
      debug:
        msg: "WARNING: Disk usage is high at {{ disk_usage }}%. Consider cleaning up disk space."
      when: disk_usage > 50

    - name: Warn about high load average
      debug:
        msg: "WARNING: Load average is high at {{ load_avg }}. Check for resource-intensive processes."
      when: load_avg > 1.0

    - name: Report healthy system
      debug:
        msg: "System is healthy. All metrics within normal ranges."
      when: disk_usage <= 50 and load_avg <= 1.0

This playbook:

  1. Executes the system_stats.sh script
  2. Parses the disk usage and load average values
  3. Displays different messages based on the values:
    • A warning if disk usage is over 50%
    • A warning if load average is over 1.0
    • A "healthy system" message if all metrics are within normal ranges

Let us run this playbook:

ansible-playbook -i inventory conditional_actions.yml

The output will depend on your system's current state, but it should include conditional messages based on your disk usage and load average.

These examples demonstrate how you can:

  1. Parse and extract specific information from script output
  2. Use that information to make decisions in your Ansible playbooks
  3. Take different actions based on the script output

These techniques are essential for creating dynamic, responsive automation workflows that can adapt to different conditions and scenarios.

Real-World Use Case - System Health Check

In this final step, we will create a complete real-world example that brings together everything we have learned about capturing and processing script output with Ansible. We will build a system health check tool that:

  1. Collects various system metrics
  2. Analyzes the metrics to identify potential issues
  3. Generates a health report
  4. Takes remedial actions when necessary

Creating the Health Check Script

First, let us create a comprehensive health check script that collects various system metrics.

Create a new file named health_check.sh in the scripts directory:

  1. Click on the "File" menu
  2. Select "New File"
  3. Save it as scripts/health_check.sh in the ~/project/ansible-output-demo directory

Add the following content to the health_check.sh file:

#!/bin/bash

## System Health Check Script

echo "HEALTH_CHECK_START: $(date)"

## CPU load
cpu_load=$(uptime | awk -F'load average: ' '{print $2}' | cut -d, -f1)
echo "CPU_LOAD: $cpu_load"
if (($(echo "$cpu_load > 1.0" | bc -l))); then
  echo "CPU_STATUS: WARNING"
else
  echo "CPU_STATUS: OK"
fi

## Memory usage
mem_total=$(free | grep Mem | awk '{print $2}')
mem_used=$(free | grep Mem | awk '{print $3}')
mem_pct=$(echo "scale=2; $mem_used / $mem_total * 100" | bc)
echo "MEM_USAGE_PCT: $mem_pct"
if (($(echo "$mem_pct > 80" | bc -l))); then
  echo "MEM_STATUS: WARNING"
else
  echo "MEM_STATUS: OK"
fi

## Disk usage
disk_usage=$(df -h / | tail -1 | awk '{print $5}' | tr -d '%')
echo "DISK_USAGE_PCT: $disk_usage"
if [ "$disk_usage" -gt 80 ]; then
  echo "DISK_STATUS: WARNING"
else
  echo "DISK_STATUS: OK"
fi

## Check for zombie processes
zombie_count=$(ps aux | grep -c Z)
echo "ZOMBIE_PROCESSES: $zombie_count"
if [ "$zombie_count" -gt 0 ]; then
  echo "ZOMBIE_STATUS: WARNING"
else
  echo "ZOMBIE_STATUS: OK"
fi

## Check system uptime
uptime_days=$(uptime | awk '{print $3}')
echo "UPTIME_DAYS: $uptime_days"

## Check last 5 log entries for errors
echo "RECENT_ERRORS: $(grep -i error /var/log/syslog 2> /dev/null | tail -5 | wc -l)"

echo "HEALTH_CHECK_END: $(date)"

Make the script executable:

chmod +x ~/project/ansible-output-demo/scripts/health_check.sh

Creating the Health Check Playbook

Now, let us create a comprehensive playbook that executes the health check script, analyzes the results, and takes appropriate actions based on the findings.

Create a new file named system_health_check.yml in the ~/project/ansible-output-demo directory:

  1. Click on the "File" menu
  2. Select "New File"
  3. Save it as system_health_check.yml in the ~/project/ansible-output-demo directory

Add the following content to the system_health_check.yml file:

---
- name: System Health Check
  hosts: local
  gather_facts: no

  tasks:
    - name: Execute health check script
      command: "{{ playbook_dir }}/scripts/health_check.sh"
      register: health_check

    - name: Display raw health check output
      debug:
        var: health_check.stdout_lines

    ## Parse metrics from the health check output
    - name: Parse CPU load
      set_fact:
        cpu_load: "{{ health_check.stdout | regex_search('CPU_LOAD: ([0-9.]+)', '\\1') | first | float }}"
        cpu_status: "{{ health_check.stdout | regex_search('CPU_STATUS: (\\w+)', '\\1') | first }}"

    - name: Parse memory usage
      set_fact:
        mem_usage: "{{ health_check.stdout | regex_search('MEM_USAGE_PCT: ([0-9.]+)', '\\1') | first | float }}"
        mem_status: "{{ health_check.stdout | regex_search('MEM_STATUS: (\\w+)', '\\1') | first }}"

    - name: Parse disk usage
      set_fact:
        disk_usage: "{{ health_check.stdout | regex_search('DISK_USAGE_PCT: (\\d+)', '\\1') | first | int }}"
        disk_status: "{{ health_check.stdout | regex_search('DISK_STATUS: (\\w+)', '\\1') | first }}"

    - name: Parse zombie processes
      set_fact:
        zombie_count: "{{ health_check.stdout | regex_search('ZOMBIE_PROCESSES: (\\d+)', '\\1') | first | int }}"
        zombie_status: "{{ health_check.stdout | regex_search('ZOMBIE_STATUS: (\\w+)', '\\1') | first }}"

    ## Generate a health status summary
    - name: Generate health status summary
      set_fact:
        health_status:
          cpu:
            load: "{{ cpu_load }}"
            status: "{{ cpu_status }}"
          memory:
            usage_percent: "{{ mem_usage }}"
            status: "{{ mem_status }}"
          disk:
            usage_percent: "{{ disk_usage }}"
            status: "{{ disk_status }}"
          processes:
            zombie_count: "{{ zombie_count }}"
            status: "{{ zombie_status }}"

    ## Display health summary
    - name: Display health summary
      debug:
        var: health_status

    ## Check overall system status
    - name: Determine overall system status
      set_fact:
        overall_status: "{{ 'WARNING' if (cpu_status == 'WARNING' or mem_status == 'WARNING' or disk_status == 'WARNING' or zombie_status == 'WARNING') else 'OK' }}"

    - name: Display overall system status
      debug:
        msg: "Overall System Status: {{ overall_status }}"

    ## Take remedial actions if necessary
    - name: Recommend actions for CPU issues
      debug:
        msg: "Action recommended: Check for resource-intensive processes using 'top' or 'htop'"
      when: cpu_status == "WARNING"

    - name: Recommend actions for memory issues
      debug:
        msg: "Action recommended: Free up memory by restarting services or clearing cache"
      when: mem_status == "WARNING"

    - name: Recommend actions for disk issues
      debug:
        msg: "Action recommended: Clean up disk space using 'du -sh /* | sort -hr' to identify large directories"
      when: disk_status == "WARNING"

    - name: Recommend actions for zombie processes
      debug:
        msg: "Action recommended: Identify and restart parent processes of zombies"
      when: zombie_status == "WARNING"

    ## Generate health report file
    - name: Create health report directory
      file:
        path: "{{ playbook_dir }}/reports"
        state: directory

    - name: Get current timestamp
      command: date "+%Y%m%d_%H%M%S"
      register: timestamp

    - name: Create health report file
      copy:
        content: |
          System Health Report - {{ timestamp.stdout }}
          ----------------------------------------------

          CPU:
            Load Average: {{ cpu_load }}
            Status: {{ cpu_status }}

          Memory:
            Usage: {{ mem_usage }}%
            Status: {{ mem_status }}

          Disk:
            Usage: {{ disk_usage }}%
            Status: {{ disk_status }}

          Processes:
            Zombie Count: {{ zombie_count }}
            Status: {{ zombie_status }}

          Overall Status: {{ overall_status }}

          Recommendations:
          {% if cpu_status == "WARNING" %}
          - CPU: Check for resource-intensive processes using 'top' or 'htop'
          {% endif %}
          {% if mem_status == "WARNING" %}
          - Memory: Free up memory by restarting services or clearing cache
          {% endif %}
          {% if disk_status == "WARNING" %}
          - Disk: Clean up disk space using 'du -sh /* | sort -hr' to identify large directories
          {% endif %}
          {% if zombie_status == "WARNING" %}
          - Processes: Identify and restart parent processes of zombies
          {% endif %}
          {% if overall_status == "OK" %}
          - System is healthy. No actions required.
          {% endif %}
        dest: "{{ playbook_dir }}/reports/health_report_{{ timestamp.stdout }}.txt"

    - name: Display report location
      debug:
        msg: "Health report saved to {{ playbook_dir }}/reports/health_report_{{ timestamp.stdout }}.txt"

This comprehensive playbook:

  1. Executes our health check script
  2. Parses the various metrics from the script output
  3. Creates a structured summary of the system health
  4. Determines the overall system status based on individual component statuses
  5. Provides specific recommendations for any issues detected
  6. Generates a detailed health report file with timestamp

Running the Health Check

Let us run our system health check playbook:

cd ~/project/ansible-output-demo
ansible-playbook -i inventory system_health_check.yml

You should see a detailed output showing the system's health status, along with any necessary recommendations for improvement. The output will vary depending on your system's current state.

After running the playbook, check the reports directory to see the generated health report:

ls -l ~/project/ansible-output-demo/reports/

You should see a file named health_report_[timestamp].txt. View the contents of this file:

cat ~/project/ansible-output-demo/reports/health_report_*.txt

Summary of What We Learned

Throughout this tutorial, we have learned:

  1. How to capture different types of output (stdout, stderr, return codes) from scripts executed by Ansible
  2. How to parse and extract specific information from script output using Ansible filters
  3. How to use script output to make decisions and perform conditional actions
  4. How to implement a complete real-world solution that leverages script output for system health monitoring

These techniques are powerful tools in your Ansible automation toolkit, enabling you to create sophisticated, dynamic, and responsive automation workflows.

Summary

In this lab, we explored how to effectively capture and utilize the output of scripts executed through Ansible. We started with basic output capture using the register keyword and progressed to more advanced techniques such as parsing output with filters and making decisions based on script results.

Key takeaways from this tutorial include:

  1. The ability to capture different types of output (stdout, stderr, return codes) from scripts executed by Ansible
  2. Techniques for parsing and extracting specific information from script output
  3. Methods for conditionally executing tasks based on script output
  4. A comprehensive real-world example demonstrating how to build a system health monitoring solution with Ansible

By mastering these techniques, you can create more sophisticated, dynamic, and responsive automation workflows that can adapt to different conditions and scenarios. This capability is essential for effective infrastructure management, application deployment, and system administration using Ansible.

As you continue your Ansible journey, remember that script output capture is just one of many powerful features that Ansible offers. Exploring other Ansible capabilities such as roles, templates, and vault will further enhance your automation toolkit.