Вопрос или проблема
Я использую 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’} действительно существует и доступен.
Как возможные решения:
-
Использование 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 %}
-
Корректное создание списка данных:
Для того чтобы использовать данные из разных хостов в одном отчете, следует создать обобщенный список с необходимой информацией в одном из этапов выполнения:
- 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
-
Генерация единого файла отчета:
После сбора данных со всех хостов стоит объединить их в одном плейбуке:
- 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, обеспечивая успешное выполнение задач и минимальное число ошибок, связанных с неопределенностью переменных.