Проблема шаблона Ansible Jinja2 с выводом регистрации, используя цикл fo

Вопрос или проблема

Я использую ansible для получения статуса нескольких сервисов и генерации HTML-вывода с помощью шаблона jinja ansible, я получаю ошибку “неопределенная переменная” или другую ошибку.

Здесь я сохраняю значения в модуле register, а затем получаю эти значения в шаблоне, но это не работает.

Шаблон Jinja:

{% for network_switch in ['client'] %}
        <tr>
            <td>{{ hostvars[network_switch]['ansible_hostname'] }}</td>
            <td>{{ hostvars[network_switch]['kernel.stdout'] }}</td>
            <td>{{ hostvars[network_switch]['httpd.stdout'] }}</td>
        </tr>
{% endfor %}

Детали Playbook:

- name: Getting the OS Information
      command: 'uname -r'
      register: kernel

    - name: Getting the OS Information
      shell: "systemctl status sshd | grep -i active | awk '{print$3}'"
      register: httpd

    - name: create HTML report
      template:
        src: report.j2
        dest: "{{ file_path }}"
      delegate_to: localhost
      run_once: true

Ошибка:

FAILED! => {“changed”: false, “msg”: “AnsibleUndefinedVariable: \”hostvars[‘client’]\” is undefined”}

Используйте ansible_play_batch “Список активных хостов в текущем запуске play, ограниченный serial, также известный как ‘batch’. Неудачные/недоступные хосты не считаются ‘активными’.”

{% for network_switch in ansible_play_batch %}
...

Если есть больше batch, файл будет перезаписан. См. Контроль выполнения playbook: стратегии и другие.
См. Переменная date в Ansible, как использовать date и создавать уникальные имена файлов. Например

dest: "{{ file_path ~ '-' ~
          ansible_date_time.date ~ '-' ~
          ansible_date_time.hour ~ '-' ~
          ansible_date_time.minute ~ '-' ~
          ansible_date_time.second }}"

Создать единый файл

Следующим вариантом будет создать список со всеми элементами в первом play и записать файл во втором play (измените шаблон и используйте my_list). Например

- hosts: all
  tasks:
    - name: Getting the OS Information
      ...
    - name: Collect the list
      set_fact:
        my_list: "{{ my_list|default([]) +
                     [hostvars[item]['ansible_hostname'],
                      hostvars[item]['kernel.stdout'],
                      hostvars[item]['httpd.stdout']] }}"
       loop: "{{ ansible_play_batch }}"
       run_once: true

- hosts: all
  tasks:
    - name: Take a look at what was collected
      debug:
        var: my_list
      run_once: true
    - name: create HTML report
      template:
        src: report.j2
        dest: "{{ file_path }}"
      delegate_to: localhost
      run_once: true

(не тестировалось)

Playbook:

---
- name: build Centos inventory report
  hosts: client

  vars:
    file_path: /var/www/html/generated_report.html

  tasks:
    #- name: Getting the OS Information
    #  command: 'cat /etc/redhat-release'
    #  register: os_release

    - name: Getting the OS Information
      command: 'uname -r'
      register: kernel

    - name: Getting the OS Information
      shell: "systemctl status sshd | grep -i active | awk '{print$3}'"
      register: httpd

    - name: Collect the list
      set_fact:
        my_list: "{{ my_list|default([]) +
                     [hostvars[item]['ansible_hostname'],
                      hostvars[item]['kernel.stdout'],
                      hostvars[item]['httpd.stdout']] }}"
      loop: "{{ ansible_play_batch }}"
      run_once: true

- name: Collecting all information
  hosts: client
  vars:
    file_path: /var/www/html/generated_report.html
  tasks:
    - name: Take a look at what was collected
      debug:
        var: my_list
      run_once: true

    - name: create HTML report
      template:
        src: report.j2
        dest: "{{ file_path }}"
      delegate_to: localhost
      run_once: true

Шаблон Jinja:

{% for network_switch in ansible_play_batch %}
        <tr>
            <td>{{ hostvars[network_switch]['ansible_hostname'] }}</td>
            <td>{{ hostvars[network_switch]['kernel.stdout'] }}</td>
            <td>{{ hostvars[network_switch]['httpd.stdout'] }}</td>
        </tr>
{% endfor %}

Ответ или решение

При решении задачи, связанной с созданием HTML отчета о статусе сервисов через Ansible и Jinja2, часто встречаются трудности с обработкой переменных и циклов. Основная ошибка, с которой сталкивается пользователь в вашем случае, связана с неопределенностью переменной, что указывает на неправильное использование переменных в шаблоне Jinja2. Давайте разберемся в этом вопросе более подробно и предложим возможные решения.

Теория

Ansible — это инструмент для автоматизации, ориентированный на управление конфигурациями и развертывание программного обеспечения. Его особенностью является использование простого и понятного синтаксиса, основанного на YAML, для описания процессов. Одна из сильных сторон Ansible — это темплейты Jinja2, которые позволяют создавать динамические файлы на основе переменных и данных, собранных во время выполнения плейбуков.

Ошибки с переменными:

Часто ошибки возникают при работе с переменными в Jinja2, если переменные не передаются правильно или неправильно обрабатываются. В Ansible важно корректно использовать контекст переменных, таких как hostvars, groups, vars и других. Ошибка, подобная "AnsibleUndefinedVariable", как правило, указывает на то, что упомянутые данные не были переданы в контекст выполнения или были неправильно вызваны из него.

Пример

Давайте рассмотрим ваш пример. Ошибка заключается в следующем: hostvars['client'] не определены. Это происходит из-за того, что Ansible не может найти ключ client в hostvars. По умолчанию hostvars содержат информацию обо всех хостах, но необходимо убедиться, что {‘client’} действительно существует и доступен.

Как возможные решения:

  1. Использование ansible_play_batch:

    Ansible предоставляет специальную переменную ansible_play_batch, которая содержит список активных хостов в текущем запуске плейбука. Это исключает необходимость жестко закодированных значений хостов.

    {% for network_switch in ansible_play_batch %}
       <tr>
           <td>{{ hostvars[network_switch]['ansible_hostname'] }}</td>
           <td>{{ hostvars[network_switch].kernel.stdout }}</td>
           <td>{{ hostvars[network_switch].httpd.stdout }}</td>
       </tr>
    {% endfor %}
  2. Корректное создание списка данных:

    Для того чтобы использовать данные из разных хостов в одном отчете, следует создать обобщенный список с необходимой информацией в одном из этапов выполнения:

    - name: Collect the list
     set_fact:
       my_list: "{{ my_list|default([]) +
                    [{'hostname': hostvars[item]['ansible_hostname'],
                      'kernel': hostvars[item].kernel.stdout,
                      'httpd': hostvars[item].httpd.stdout}] }}"
     loop: "{{ ansible_play_batch }}"
     run_once: true
  3. Генерация единого файла отчета:

    После сбора данных со всех хостов стоит объединить их в одном плейбуке:

    - name: create HTML report
     template:
       src: report.j2
       dest: "{{ file_path }}"
     delegate_to: localhost
     run_once: true

Применение

После интеграции этих предложений, вы сможете обрабатывать данные всех хостов активного запуска. Каждый элемент ansible_play_batch гарантированно содержит доступные параметры, предоставленные hostvars. Это снижает риск возникновения неопределенных переменных и ошибок выполнения.

Таким образом, сложным аспектом здесь является необходимость внимательного подхода к конфигурации переменных и управлению контекстом их использования в плейбуках Ansible. Это требует знания двух ключевых аспектов: как Ansible обрабатывает данные во время выполнения и каким образом эти данные могут быть переданы и использованы внутри Jinja2 шаблонов.

Также, к данному стандартному процессу можно добавить автоматизированное создание уникальных имен файлов, чтобы избежать их перезаписи в случае выполнения с чёткой регулярностью или пачками:

dest: "{{ file_path ~ '-' ~ ansible_date_time.date ~ '-' ~ ansible_date_time.hour ~ '-' ~ ansible_date_time.minute ~ '-' ~ ansible_date_time.second }}"

Таким образом, интеграция всех этих шагов позволит создать стабильный и динамичный процесс генерации отчетов с использованием Ansible и Jinja2, обеспечивая успешное выполнение задач и минимальное число ошибок, связанных с неопределенностью переменных.

Оцените материал
Добавить комментарий

Капча загружается...