Как получить только stdout_lines с Ansible, используя модуль mail

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

Я использую Ansible для сканирования хостов с помощью антивируса Clam и получаю отчеты по электронной почте. Отчеты содержат все, и я хочу получать только строки stdout. Есть ли способ достичь этого? Я написал шаблон электронной почты в соответствии с https://gist.github.com/halberom/0aea275632d2b47af0536e5def01d4d2, но единственное, что отличается, это то, что я использовал nice json:

The {{ host }} says {{ hostvars[host]['result']['stdout'] | to_nice_json }}

Ошибка, которую я получаю:

Не удается преобразовать данные с помощью to_nice_json, возвращаясь к to_json: у объекта ‘dict’ нет атрибута ‘stdout’. Задача включает опцию с неопределенной переменной. Ошибка была ansible no_log:false

Если я удаляю ['stdout'], то отчет по электронной почте выглядит так:

Сканирование ClamAV было выполнено на сервере host_server
{
    "changed": true,
    "msg": "Все элементы завершены",
    "results": [
        {
            "ansible_loop_var": "item",
            "changed": true,
            "cmd": [
                "clamscan",
                "-r",
                "-i",
                "/usr/bin"
            ],
            "delta": "0:00:37.293719",
            "end": "2021-09-09 18:47:55.626094",
            "failed": false,
            "invocation": {
                "module_args": {
                    "_raw_params": "clamscan -r -i /usr/bin",
                    "_uses_shell": false,
                    "argv": null,
                    "chdir": null,
                    "creates": null,
                    "executable": null,
                    "removes": null,
                    "stdin": null,
                    "stdin_add_newline": true,
                    "strip_empty_ends": true,
                    "warn": true
                }
            },
            "item": "/usr/bin",
            "rc": 0,
            "start": "2021-09-09 18:47:18.332375",
            "stderr": "",
            "stderr_lines": [],
            "stdout": "\n----------- РЕЗЮМЕ СКАНИРОВАНИЯ -----------\nИзвестные вирусы: 8563204\nВерсия движка: 0.103.2\nСканированные каталоги: 1\nСканированные файлы: 701\nИнфицированные файлы: 0\nПросканированные данные: 110.08 MB\nПрочитанные данные: 109.50 MB (соотношение 1.01:1)\nВремя: 37.113 сек (0 м 37 с)\nДата начала: 2021:09:09 18:47:18\nДата окончания: 2021:09:09 18:47:55",
            "stdout_lines": [
                "",
                "----------- РЕЗЮМЕ СКАНИРОВАНИЯ -----------",
                "Известные вирусы: 8563204",
                "Версия движка: 0.103.2",
                "Сканированные каталоги: 1",
                "Сканированные файлы: 701",
                "Инфицированные файлы: 0",
                "Просканированные данные: 110.08 MB",
                "Прочитанные данные: 109.50 MB (соотношение 1.01:1)",
                "Время: 37.113 сек (0 м 37 с)",
                "Дата начала: 2021:09:09 18:47:18",
                "Дата окончания: 2021:09:09 18:47:55 "
            ]
        }
    ]
}

Есть ли способ получать только stdout_lines в моем отчете? Я пробовал использовать grep в командной оболочке, но не удалось

Из вашего отладочного вывода, result является зарегистрированной переменной из задания shell, где выполняется clamav в цикле по списку путей.

Как описано в документации по циклам Ansible, зарегистрированная переменная модифицируется и содержит список results, где каждый элемент является индивидуальным результатом выполнения модуля с текущим итерационным элементом (например, /usr/bin в вашем примере).

В этом случае result.stdout не существует (как ясно сообщается в вашем сообщении об ошибке), но в вашем примере “result.results.0.stdout” существует.

Хотя это будет работать, если у вас всегда в цикле только один путь, вероятно, вы хотите получить список всех stdout из каждого результата. В этом случае вы можете использовать:

result.results | map(attribute="stdout")

Если мы вставим это обратно в ваш конкретный шаблон, способный выдавать вывод для любого данного host, это будет выглядеть следующим образом:

The {{ host }} says {{ hostvars[host].result.results | map(attribute="stdout") | to_nice_json }}

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

Это отчет для хоста {{ host }}
----------------------------------
{% for scan_result in hostvars[host].result.results %}
Сканирование выполнено в каталоге {{ scan_result.item }}:
{{ scan_result.stdout }}
{% endfor %}

.

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

Чтобы получить только stdout_lines в отчете Ansible, необходимо внимательно настроить обработку результатов выполнения задачи сканирования с помощью ClamAV и модуль отправки электронной почты. Давайте разберем, как это можно реализовать.

Теория

В Ansible, когда вы используете цикл для выполнения команды на нескольких путях, переменная, в которую регистрируется результат выполнения задачи, содержит список результатов (results). Каждый элемент этого списка представляет собой результат выполнения команды для одного из путей. Для получения строк вывода (stdout_lines) из каждого результата, нужно обратиться к этому списку и выбрать соответствующее значение.

Пример

В вашей настройке сообщения хотите, чтобы отчет содержал только stdout_lines. Переменная result, которая регистрируется в вашей задаче, имеет структуру, включающую такие поля, как stdout, stderr, stdout_lines и другие для каждого элемента.

Предположим, что result — это регистрируемая переменная из задачи оболочки, которая выполняет команды антивирусного сканирования в цикле по списку путей. Вот как можно получить нужные данные:

- name: Сканирование с помощью ClamAV
  shell: clamscan -r -i {{ item }}
  register: result
  loop:
    - /usr/bin
    # Добавьте другие директории, если необходимо

- name: Отправка отчета по электронной почте
  ansible.builtin.mail:
    to: "your_email@example.com"
    subject: "Отчет ClamAV сканирования для {{ inventory_hostname }}"
    body: |
      Это отчет для хоста {{ inventory_hostname }}
      ----------------------------------------
      {% for scan_result in result.results %}
      Сканирование выполнено в директории {{ scan_result.item }}:
      {{ scan_result.stdout_lines | join('\n') }}
      {% endfor %}

Применение

На выходе вы получите хорошо структурированные письма, содержащие только строки из stdout_lines, для каждого из проверенных путей. Такой подход также позволит сохранить информацию о том, какие директории сканировались, что важно для понимания контекста сканирования.

Убедитесь, что функция join('\n') используется для преобразования списка строк в строку с разрывами, что сделает вывод более читаемым в письме.

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

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

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